[llvm] 8dc7b3d - Add WaveReadLaneFirst HLSL function (#220373)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 25 11:48:43 PDT 2026


Author: Joshua Batista
Date: 2026-09-25T11:48:34-07:00
New Revision: 8dc7b3d92cc6371d942dc7619a81faa1f56de938

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

LOG: Add WaveReadLaneFirst HLSL function (#220373)

This PR adds the `WaveReadLaneFirst` HLSL function.

Fixes https://github.com/llvm/llvm-project/issues/99164
Assisted by: Github Copilot

Added: 
    clang/test/CodeGenHLSL/builtins/WaveReadLaneFirst.hlsl
    clang/test/SemaHLSL/BuiltIns/WaveReadLaneFirst-errors.hlsl
    llvm/test/CodeGen/DirectX/LongVector/wave-readlane-first.ll
    llvm/test/CodeGen/DirectX/WaveReadLaneFirst.ll
    llvm/test/CodeGen/DirectX/WaveReadLaneFirst_mat.ll
    llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/wave-readlane-first.ll
    llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst.ll
    llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst_mat.ll

Modified: 
    clang/include/clang/Basic/Builtins.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/HLSLIntrinsics.td
    clang/lib/CodeGen/CGHLSLBuiltins.cpp
    clang/lib/CodeGen/CGHLSLRuntime.h
    clang/lib/Sema/SemaHLSL.cpp
    clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
    llvm/include/llvm/IR/IntrinsicsDirectX.td
    llvm/include/llvm/IR/IntrinsicsSPIRV.td
    llvm/lib/Target/DirectX/DXIL.td
    llvm/lib/Target/DirectX/DXILShaderFlags.cpp
    llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
    llvm/test/CodeGen/DirectX/ShaderFlags/wave-ops.ll

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 28178c0370b27..f1628c175490f 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5678,6 +5678,12 @@ def HLSLWaveReadLaneAt : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void(...)";
 }
 
+def HLSLWaveReadLaneFirst : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_wave_read_lane_first"];
+  let Attributes = [NoThrow, Const];
+  let Prototype = "void(...)";
+}
+
 def HLSLWaveGetLaneCount : LangBuiltin<"HLSL_LANG"> {
   let Spellings = ["__builtin_hlsl_wave_get_lane_count"];
   let Attributes = [NoThrow, Const];

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 01775af5d301c..d293a9798da6a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9970,8 +9970,9 @@ def err_typecheck_expect_scalar_or_vector : Error<
 def err_typecheck_expect_scalar_or_vector_or_matrix : Error<
   "invalid operand of type %0 where %1 or "
   "a vector or matrix of such type is required">;
-def err_typecheck_expect_any_scalar_or_vector : Error<
-  "invalid operand of type %0%select{| where a scalar or vector is required}1">;
+def err_typecheck_expect_any_scalar_or_vector_or_matrix : Error<
+  "invalid operand of type %0%select{| where a scalar or vector is required|"
+  " where a scalar, vector, or matrix is required}1">;
 def err_typecheck_expect_flt_or_vector : Error<
   "invalid operand of type %0 where floating, complex or "
   "a vector of such types is required">;

diff  --git a/clang/include/clang/Basic/HLSLIntrinsics.td b/clang/include/clang/Basic/HLSLIntrinsics.td
index 10adf4fb676ee..f067d060d5a2d 100644
--- a/clang/include/clang/Basic/HLSLIntrinsics.td
+++ b/clang/include/clang/Basic/HLSLIntrinsics.td
@@ -1913,3 +1913,17 @@ the specified wave.
   let Availability = SM6_0;
   let VaryingMatDims = [];
 }
+
+// Reads the value from the first active lane in the wave.
+def hlsl_wave_read_lane_first :
+    HLSLOneArgBuiltin<"WaveReadLaneFirst",
+                      "__builtin_hlsl_wave_read_lane_first"> {
+  let Doc = [{
+\brief Returns the value from the active lane with the smallest index.
+\param Val The value to read.
+}];
+  let VaryingTypes = AllTypesWithBool;
+let VaryingLongVector = 1;
+let IsConvergent = 1;
+let Availability = SM6_0;
+}

diff  --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 26220e3a732bf..fa77cbbd08192 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1597,6 +1597,12 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
                              {OpExpr->getType()}, ArrayRef{OpExpr, OpIndex},
                              "hlsl.wave.readlane");
   }
+  case Builtin::BI__builtin_hlsl_wave_read_lane_first: {
+    Value *OpExpr = EmitScalarExpr(E->getArg(0));
+    return EmitIntrinsicCall(
+        CGM.getHLSLRuntime().getWaveReadLaneFirstIntrinsic(),
+        {OpExpr->getType()}, ArrayRef{OpExpr}, "hlsl.wave.readlane.first");
+  }
   case Builtin::BI__builtin_hlsl_wave_prefix_sum: {
     Value *OpExpr = EmitScalarExpr(E->getArg(0));
     Intrinsic::ID IID = getWavePrefixSumIntrinsic(

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c44fb102598b0..11b3e0220dcfb 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -154,6 +154,7 @@ class CGHLSLRuntime {
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveIsFirstLane, wave_is_first_lane)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveGetLaneCount, wave_get_lane_count)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveReadLaneAt, wave_readlane)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(WaveReadLaneFirst, wave_readlane_first)
   GENERATE_HLSL_INTRINSIC_FUNCTION(QuadReadAcrossX, quad_read_across_x)
   GENERATE_HLSL_INTRINSIC_FUNCTION(QuadReadAcrossY, quad_read_across_y)
   GENERATE_HLSL_INTRINSIC_FUNCTION(QuadReadAcrossDiagonal,

diff  --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 124f5bd23a623..37fab2a8c3fd6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3536,13 +3536,41 @@ static bool CheckAnyScalarOrVector(Sema *S, CallExpr *TheCall,
   if (!(ArgType->isScalarType() ||
         (VTy && VTy->getElementType()->isScalarType()))) {
     S->Diag(TheCall->getArg(0)->getBeginLoc(),
-            diag::err_typecheck_expect_any_scalar_or_vector)
+            diag::err_typecheck_expect_any_scalar_or_vector_or_matrix)
         << ArgType << 1;
     return true;
   }
   return false;
 }
 
+static bool CheckAnyScalarOrVectorOrMatrix(Sema *S, CallExpr *TheCall,
+                                           unsigned ArgIndex) {
+  assert(TheCall->getNumArgs() > ArgIndex);
+  QualType ArgType = TheCall->getArg(ArgIndex)->getType();
+  if (ArgType->isDependentType())
+    return false;
+
+  QualType ElementType = ArgType;
+  if (const auto *VectorTy = ArgType->getAs<VectorType>())
+    ElementType = VectorTy->getElementType();
+  else if (const auto *MatrixTy = ArgType->getAs<ConstantMatrixType>())
+    ElementType = MatrixTy->getElementType();
+
+  if (ElementType->isBooleanType())
+    return false;
+
+  if (ElementType->isIntegerType() || ElementType->isRealFloatingType()) {
+    unsigned BitWidth = S->Context.getTypeSize(ElementType);
+    if (BitWidth == 16 || BitWidth == 32 || BitWidth == 64)
+      return false;
+  }
+
+  S->Diag(TheCall->getArg(ArgIndex)->getBeginLoc(),
+          diag::err_typecheck_expect_any_scalar_or_vector_or_matrix)
+      << ArgType << 2;
+  return true;
+}
+
 // Check that the argument is not a bool or vector<bool>
 // Returns true on error
 static bool CheckNotBoolScalarOrVector(Sema *S, CallExpr *TheCall,
@@ -3556,7 +3584,7 @@ static bool CheckNotBoolScalarOrVector(Sema *S, CallExpr *TheCall,
       (VTy &&
        S->Context.hasSameUnqualifiedType(VTy->getElementType(), BoolType))) {
     S->Diag(TheCall->getArg(0)->getBeginLoc(),
-            diag::err_typecheck_expect_any_scalar_or_vector)
+            diag::err_typecheck_expect_any_scalar_or_vector_or_matrix)
         << ArgType << 0;
     return true;
   }
@@ -4735,14 +4763,14 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
 
     if (!(ArgType->isScalarType())) {
       SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
-                   diag::err_typecheck_expect_any_scalar_or_vector)
+                   diag::err_typecheck_expect_any_scalar_or_vector_or_matrix)
           << ArgType << 0;
       return true;
     }
 
     if (!(ArgType->isBooleanType())) {
       SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
-                   diag::err_typecheck_expect_any_scalar_or_vector)
+                   diag::err_typecheck_expect_any_scalar_or_vector_or_matrix)
           << ArgType << 0;
       return true;
     }
@@ -4772,6 +4800,16 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
     TheCall->setType(ArgTyExpr);
     break;
   }
+  case Builtin::BI__builtin_hlsl_wave_read_lane_first: {
+    if (SemaRef.checkArgCount(TheCall, 1))
+      return true;
+
+    if (CheckAnyScalarOrVectorOrMatrix(&SemaRef, TheCall, 0))
+      return true;
+
+    TheCall->setType(TheCall->getArg(0)->getType());
+    break;
+  }
   case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
     if (SemaRef.checkArgCount(TheCall, 0))
       return true;

diff  --git a/clang/test/CodeGenHLSL/builtins/WaveReadLaneFirst.hlsl b/clang/test/CodeGenHLSL/builtins/WaveReadLaneFirst.hlsl
new file mode 100644
index 0000000000000..fb3bd5bbb042e
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/WaveReadLaneFirst.hlsl
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -fnative-half-type -fnative-int16-type -triple \
+// RUN:   dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s -DTARGET=dx
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -fnative-half-type -fnative-int16-type -triple \
+// RUN:   spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s -DTARGET=spv
+
+// CHECK-LABEL: test_int
+int test_int(int expr) {
+  // CHECK: %[[#entry_tok0:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok0]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK: declare [[TY]] @llvm.[[TARGET]].wave.readlane.first.i32([[TY]]) #[[#attr:]]
+
+// CHECK-LABEL: test_uint
+uint test_uint(uint expr) {
+  // CHECK: %[[#entry_tok0:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok0]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_int64_t
+int64_t test_int64_t(int64_t expr) {
+  // CHECK: %[[#entry_tok1:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i64([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok1]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK: declare [[TY]] @llvm.[[TARGET]].wave.readlane.first.i64([[TY]]) #[[#attr:]]
+
+// CHECK-LABEL: test_uint64_t
+uint64_t test_uint64_t(uint64_t expr) {
+  // CHECK: %[[#entry_tok1:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i64([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok1]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+#ifdef __HLSL_ENABLE_16_BIT
+// CHECK-LABEL: test_int16
+int16_t test_int16(int16_t expr) {
+  // CHECK: %[[#entry_tok2:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i16([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok2]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK: declare [[TY]] @llvm.[[TARGET]].wave.readlane.first.i16([[TY]]) #[[#attr:]]
+
+// CHECK-LABEL: test_uint16
+uint16_t test_uint16(uint16_t expr) {
+  // CHECK: %[[#entry_tok2:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.i16([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok2]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+#endif
+
+// CHECK-LABEL: test_bool
+bool test_bool(bool expr) {
+  // CHECK: %[[#entry_tok3:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call i1 @llvm.[[TARGET]].wave.readlane.first.i1(i1 %{{[a-zA-Z0-9]+}}) [ "convergencectrl"(token %[[#entry_tok3]]) ]
+  // CHECK: ret i1 %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_half
+half test_half(half expr) {
+  // CHECK: %[[#entry_tok4:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.f16([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok4]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_double
+double test_double(double expr) {
+  // CHECK: %[[#entry_tok5:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.f64([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok5]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_floatv4
+float4 test_floatv4(float4 expr) {
+  // CHECK: %[[#entry_tok6:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.v4f32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok6]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_floatv5
+vector<float, 5> test_floatv5(vector<float, 5> expr) {
+  // CHECK: %[[#entry_tok7:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.v5f32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok7]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_float2x3
+float2x3 test_float2x3(float2x3 expr) {
+  // CHECK: %[[#entry_tok8:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.v6f32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok8]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK-LABEL: test_float3x4
+float3x4 test_float3x4(float3x4 expr) {
+  // CHECK: %[[#entry_tok9:]] = call token @llvm.experimental.convergence.entry()
+  // CHECK: %[[RET:.*]] = call reassoc nnan ninf nsz arcp afn [[TY:.*]] @llvm.[[TARGET]].wave.readlane.first.v12f32([[TY]] %[[#]]) [ "convergencectrl"(token %[[#entry_tok9]]) ]
+  // CHECK: ret [[TY]] %[[RET]]
+  return WaveReadLaneFirst(expr);
+}
+
+// CHECK: attributes #[[#attr]] = {{{.*}} convergent {{.*}}}

diff  --git a/clang/test/SemaHLSL/BuiltIns/WaveReadLaneFirst-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/WaveReadLaneFirst-errors.hlsl
new file mode 100644
index 0000000000000..19e5fdadc276d
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/WaveReadLaneFirst-errors.hlsl
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify
+
+bool test_too_few_arg() {
+  return __builtin_hlsl_wave_read_lane_first();
+  // expected-error at -1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+float2 test_too_many_arg(float2 p0) {
+  return __builtin_hlsl_wave_read_lane_first(p0, p0);
+  // expected-error at -1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+struct S { float f; };
+
+S test_expr_struct_type_check(S p0) {
+  return __builtin_hlsl_wave_read_lane_first(p0);
+  // expected-error at -1 {{invalid operand of type 'S' where a scalar, vector, or matrix is required}}
+}
+
+enum E { A };
+
+E test_expr_enum_type_check(E p0) {
+  return __builtin_hlsl_wave_read_lane_first(p0);
+}

diff  --git a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
index 5741b81832ad0..7501d895e8163 100644
--- a/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
+++ b/clang/test/SemaHLSL/WaveBuiltinAvailability.hlsl
@@ -36,6 +36,10 @@ void foo() {
     // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{'WaveReadLaneAt' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
     float g = hlsl::WaveReadLaneAt(1.0f, 0u); // #WaveReadLaneAt
 
+    // expected-error@#WaveReadLaneFirst {{'WaveReadLaneFirst' is only available on Shader Model 6.0 or newer}}
+    // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{'WaveReadLaneFirst' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    float first = hlsl::WaveReadLaneFirst(1.0f); // #WaveReadLaneFirst
+
     // Test that half overloads (which map to float without native half) also
     // have the correct SM 6.0 availability via the _HLSL_16BIT_AVAILABILITY
     // fallback path.
@@ -46,4 +50,8 @@ void foo() {
     // expected-error@#WaveReadLaneAt_half {{'WaveReadLaneAt' is only available on Shader Model 6.0 or newer}}
     // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{'WaveReadLaneAt' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
     half i = hlsl::WaveReadLaneAt((half)1.0, 0u); // #WaveReadLaneAt_half
+
+    // expected-error@#WaveReadLaneFirst_half {{'WaveReadLaneFirst' is only available on Shader Model 6.0 or newer}}
+    // expected-note at hlsl/hlsl_alias_intrinsics_gen.inc:* {{'WaveReadLaneFirst' has been marked as being introduced in Shader Model 6.0 here, but the deployment target is Shader Model 5.0}}
+    half j = hlsl::WaveReadLaneFirst((half)1.0); // #WaveReadLaneFirst_half
 }

diff  --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 2d29165e27368..e087216b822da 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -302,6 +302,7 @@ def int_dx_wave_product : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>
 def int_dx_wave_uproduct : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
 def int_dx_wave_is_first_lane : DefaultAttrsIntrinsic<[llvm_i1_ty], [], [IntrConvergent]>;
 def int_dx_wave_readlane : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, llvm_i32_ty], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
+def int_dx_wave_readlane_first : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
 def int_dx_wave_get_lane_count
     : DefaultAttrsIntrinsic<[llvm_i32_ty], [], [IntrConvergent]>;
 def int_dx_wave_prefix_sum : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;

diff  --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index d634253b1ccbe..25b5a3c385465 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -156,6 +156,7 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
   def int_spv_wave_product : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem]>;
   def int_spv_wave_is_first_lane : DefaultAttrsIntrinsic<[llvm_i1_ty], [], [IntrConvergent]>;
   def int_spv_wave_readlane : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, llvm_i32_ty], [IntrConvergent, IntrNoMem]>;
+  def int_spv_wave_readlane_first : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem]>;
   def int_spv_wave_get_lane_count
       : DefaultAttrsIntrinsic<[llvm_i32_ty], [], [IntrConvergent]>;
   def int_spv_wave_prefix_sum : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem]>;

diff  --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index cde3a2ca8db31..5e63e94a1c069 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -1283,6 +1283,16 @@ def WaveReadLaneAt : DXILOp<117, waveReadLaneAt> {
   let stages = [Stages<DXIL1_0, [all_stages]>];
 }
 
+def WaveReadLaneFirst : DXILOp<118, waveReadLaneFirst> {
+  let Doc = "returns the value from the first active lane";
+  let intrinsics = [IntrinSelect<int_dx_wave_readlane_first>];
+  let arguments = [OverloadTy];
+  let result = OverloadTy;
+  let overloads = [Overloads<
+      DXIL1_0, [HalfTy, FloatTy, DoubleTy, Int1Ty, Int16Ty, Int32Ty, Int64Ty]>];
+  let stages = [Stages<DXIL1_0, [all_stages]>];
+}
+
 def WaveActiveOp : DXILOp<119, waveActiveOp> {
   let Doc = "returns the result of the operation across waves";
   let intrinsics = [

diff  --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
index bc69af6f73cdd..10b7a659c6d5d 100644
--- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
+++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp
@@ -89,6 +89,7 @@ static bool checkWaveOps(Intrinsic::ID IID) {
   case Intrinsic::dx_wave_all_equal:
   case Intrinsic::dx_wave_all:
   case Intrinsic::dx_wave_readlane:
+  case Intrinsic::dx_wave_readlane_first:
   case Intrinsic::dx_wave_active_countbits:
   case Intrinsic::dx_wave_ballot:
   case Intrinsic::dx_wave_prefix_bit_count:

diff  --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 562344ac52b58..507cc599b801a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5721,6 +5721,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
   case Intrinsic::spv_wave_readlane:
     return selectWaveOpInst(ResVReg, ResType, I,
                             SPIRV::OpGroupNonUniformShuffle);
+  case Intrinsic::spv_wave_readlane_first:
+    return selectWaveOpInst(ResVReg, ResType, I,
+                            SPIRV::OpGroupNonUniformBroadcastFirst);
   case Intrinsic::spv_wave_prefix_sum:
     return selectWaveExclusiveScanSum(ResVReg, ResType, I);
   case Intrinsic::spv_wave_prefix_product:

diff  --git a/llvm/test/CodeGen/DirectX/LongVector/wave-readlane-first.ll b/llvm/test/CodeGen/DirectX/LongVector/wave-readlane-first.ll
new file mode 100644
index 0000000000000..fa55942bce146
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/LongVector/wave-readlane-first.ll
@@ -0,0 +1,13 @@
+; RUN: llc -mtriple=dxil-pc-shadermodel6.8-library -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SCALAR
+; RUN: llc -mtriple=dxil-pc-shadermodel6.9-library -stop-before=dxil-op-lower -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-VECTOR
+
+; CHECK-LABEL: define <5 x float> @wave_readlane_first_v5float(
+; CHECK-SCALAR-COUNT-5: call float @dx.op.waveReadLaneFirst.f32(i32 118,
+; CHECK-VECTOR: call <5 x float> @llvm.dx.wave.readlane.first.v5f32
+define <5 x float> @wave_readlane_first_v5float(<5 x float> %expr) {
+  %ret = call <5 x float> @llvm.dx.wave.readlane.first.v5f32(
+      <5 x float> %expr)
+  ret <5 x float> %ret
+}
+
+declare <5 x float> @llvm.dx.wave.readlane.first.v5f32(<5 x float>)

diff  --git a/llvm/test/CodeGen/DirectX/ShaderFlags/wave-ops.ll b/llvm/test/CodeGen/DirectX/ShaderFlags/wave-ops.ll
index 77478add84caf..37a991e1c1fa8 100644
--- a/llvm/test/CodeGen/DirectX/ShaderFlags/wave-ops.ll
+++ b/llvm/test/CodeGen/DirectX/ShaderFlags/wave-ops.ll
@@ -84,6 +84,13 @@ entry:
   ret i1 %ret
 }
 
+define noundef i1 @wave_readlane_first(i1 %x) {
+entry:
+  ; CHECK: Function wave_readlane_first : [[WAVE_FLAG]]
+  %ret = call i1 @llvm.dx.wave.readlane.first.i1(i1 %x)
+  ret i1 %ret
+}
+
 define noundef i32 @wave_reduce_sum(i32 noundef %x) {
 entry:
   ; CHECK: Function wave_reduce_sum : [[WAVE_FLAG]]

diff  --git a/llvm/test/CodeGen/DirectX/WaveReadLaneFirst.ll b/llvm/test/CodeGen/DirectX/WaveReadLaneFirst.ll
new file mode 100644
index 0000000000000..0a68455aa166e
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/WaveReadLaneFirst.ll
@@ -0,0 +1,96 @@
+; RUN: opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-compute %s | FileCheck %s
+
+; Test that WaveReadLaneFirst maps down to the DirectX op.
+
+define noundef half @wave_readlane_first_half(half noundef %expr) {
+entry:
+; CHECK: call half @dx.op.waveReadLaneFirst.f16(i32 118, half %expr)
+  %ret = call half @llvm.dx.wave.readlane.first.f16(half %expr)
+  ret half %ret
+}
+
+define noundef float @wave_readlane_first_float(float noundef %expr) {
+entry:
+; CHECK: call float @dx.op.waveReadLaneFirst.f32(i32 118, float %expr)
+  %ret = call float @llvm.dx.wave.readlane.first.f32(float %expr)
+  ret float %ret
+}
+
+define noundef double @wave_readlane_first_double(double noundef %expr) {
+entry:
+; CHECK: call double @dx.op.waveReadLaneFirst.f64(i32 118, double %expr)
+  %ret = call double @llvm.dx.wave.readlane.first.f64(double %expr)
+  ret double %ret
+}
+
+define noundef i1 @wave_readlane_first_i1(i1 noundef %expr) {
+entry:
+; CHECK: call i1 @dx.op.waveReadLaneFirst.i1(i32 118, i1 %expr)
+  %ret = call i1 @llvm.dx.wave.readlane.first.i1(i1 %expr)
+  ret i1 %ret
+}
+
+define noundef i16 @wave_readlane_first_i16(i16 noundef %expr) {
+entry:
+; CHECK: call i16 @dx.op.waveReadLaneFirst.i16(i32 118, i16 %expr)
+  %ret = call i16 @llvm.dx.wave.readlane.first.i16(i16 %expr)
+  ret i16 %ret
+}
+
+define noundef i32 @wave_readlane_first_i32(i32 noundef %expr) {
+entry:
+; CHECK: call i32 @dx.op.waveReadLaneFirst.i32(i32 118, i32 %expr)
+  %ret = call i32 @llvm.dx.wave.readlane.first.i32(i32 %expr)
+  ret i32 %ret
+}
+
+define noundef i64 @wave_readlane_first_i64(i64 noundef %expr) {
+entry:
+; CHECK: call i64 @dx.op.waveReadLaneFirst.i64(i32 118, i64 %expr)
+  %ret = call i64 @llvm.dx.wave.readlane.first.i64(i64 %expr)
+  ret i64 %ret
+}
+
+define noundef <2 x half> @wave_readlane_first_v2half(
+    <2 x half> noundef %expr) {
+entry:
+; CHECK: call half @dx.op.waveReadLaneFirst.f16(i32 118, half %expr.i0)
+; CHECK: call half @dx.op.waveReadLaneFirst.f16(i32 118, half %expr.i1)
+  %ret = call <2 x half> @llvm.dx.wave.readlane.first.v2f16(
+      <2 x half> %expr)
+  ret <2 x half> %ret
+}
+
+define noundef <3 x i32> @wave_readlane_first_v3i32(
+    <3 x i32> noundef %expr) {
+entry:
+; CHECK: call i32 @dx.op.waveReadLaneFirst.i32(i32 118, i32 %expr.i0)
+; CHECK: call i32 @dx.op.waveReadLaneFirst.i32(i32 118, i32 %expr.i1)
+; CHECK: call i32 @dx.op.waveReadLaneFirst.i32(i32 118, i32 %expr.i2)
+  %ret = call <3 x i32> @llvm.dx.wave.readlane.first.v3i32(
+      <3 x i32> %expr)
+  ret <3 x i32> %ret
+}
+
+define noundef <4 x float> @wave_readlane_first_v4float(
+    <4 x float> noundef %expr) {
+entry:
+; CHECK: call float @dx.op.waveReadLaneFirst.f32(i32 118, float %expr.i0)
+; CHECK: call float @dx.op.waveReadLaneFirst.f32(i32 118, float %expr.i1)
+; CHECK: call float @dx.op.waveReadLaneFirst.f32(i32 118, float %expr.i2)
+; CHECK: call float @dx.op.waveReadLaneFirst.f32(i32 118, float %expr.i3)
+  %ret = call <4 x float> @llvm.dx.wave.readlane.first.v4f32(
+      <4 x float> %expr)
+  ret <4 x float> %ret
+}
+
+declare half @llvm.dx.wave.readlane.first.f16(half)
+declare float @llvm.dx.wave.readlane.first.f32(float)
+declare double @llvm.dx.wave.readlane.first.f64(double)
+declare i1 @llvm.dx.wave.readlane.first.i1(i1)
+declare i16 @llvm.dx.wave.readlane.first.i16(i16)
+declare i32 @llvm.dx.wave.readlane.first.i32(i32)
+declare i64 @llvm.dx.wave.readlane.first.i64(i64)
+declare <2 x half> @llvm.dx.wave.readlane.first.v2f16(<2 x half>)
+declare <3 x i32> @llvm.dx.wave.readlane.first.v3i32(<3 x i32>)
+declare <4 x float> @llvm.dx.wave.readlane.first.v4f32(<4 x float>)

diff  --git a/llvm/test/CodeGen/DirectX/WaveReadLaneFirst_mat.ll b/llvm/test/CodeGen/DirectX/WaveReadLaneFirst_mat.ll
new file mode 100644
index 0000000000000..cde610839c291
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/WaveReadLaneFirst_mat.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-compute %s | FileCheck %s
+
+; Test WaveReadLaneFirst scalarization for matrix values.
+
+define noundef <6 x float> @wave_readlane_first_float2x3(
+    <6 x float> noundef %expr) {
+entry:
+; CHECK-LABEL: define noundef <6 x float> @wave_readlane_first_float2x3(
+; CHECK-COUNT-6: call float @dx.op.waveReadLaneFirst.f32(i32 118,
+  %ret = call <6 x float> @llvm.dx.wave.readlane.first.v6f32(
+      <6 x float> %expr)
+  ret <6 x float> %ret
+}
+
+define noundef <12 x float> @wave_readlane_first_float3x4(
+    <12 x float> noundef %expr) {
+entry:
+; CHECK-LABEL: define noundef <12 x float> @wave_readlane_first_float3x4(
+; CHECK-COUNT-12: call float @dx.op.waveReadLaneFirst.f32(i32 118,
+  %ret = call <12 x float> @llvm.dx.wave.readlane.first.v12f32(
+      <12 x float> %expr)
+  ret <12 x float> %ret
+}
+
+declare <6 x float> @llvm.dx.wave.readlane.first.v6f32(<6 x float>)
+declare <12 x float> @llvm.dx.wave.readlane.first.v12f32(<12 x float>)

diff  --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/wave-readlane-first.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/wave-readlane-first.ll
new file mode 100644
index 0000000000000..63b00fdbed988
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_EXT_long_vector/wave-readlane-first.ll
@@ -0,0 +1,36 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute --spirv-ext=+SPV_EXT_long_vector %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute --spirv-ext=+SPV_EXT_long_vector %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %}
+
+; Test that SPV_EXT_long_vector preserves a WaveReadLaneFirst long vector.
+
+; CHECK-DAG: Capability Shader
+; CHECK-DAG: Capability GroupNonUniformBallot
+; CHECK-DAG: Capability LongVectorEXT
+; CHECK-DAG: Extension "SPV_EXT_long_vector"
+
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#f32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#scope:]] = OpConstant %[[#uint]] 3
+; CHECK-DAG: %[[#size5:]] = OpConstant %[[#uint]] 5
+; CHECK-DAG: %[[#v5_float:]] = OpTypeVectorIdEXT %[[#f32]] %[[#size5]]
+
+ at wide_f32_5 = internal addrspace(10) global [5 x float] zeroinitializer
+
+; CHECK-LABEL: Begin function test_floatv5
+define internal void @test_floatv5() {
+entry:
+  %expr = load <5 x float>, ptr addrspace(10) @wide_f32_5
+; CHECK: OpGroupNonUniformBroadcastFirst %[[#v5_float]] %[[#scope]]
+  %result = call <5 x float> @llvm.spv.wave.readlane.first.v5f32(
+      <5 x float> %expr)
+  store <5 x float> %result, ptr addrspace(10) @wide_f32_5
+  ret void
+}
+
+define void @main() #0 {
+  ret void
+}
+
+declare <5 x float> @llvm.spv.wave.readlane.first.v5f32(<5 x float>)
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }

diff  --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst.ll
new file mode 100644
index 0000000000000..e6b111d8f197f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst.ll
@@ -0,0 +1,61 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %}
+
+; Test WaveReadLaneFirst lowering for scalar and vector types.
+
+; CHECK: Capability Shader
+; CHECK: Capability GroupNonUniformBallot
+
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#f32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#v4_float:]] = OpTypeVector %[[#f32]] 4
+; CHECK-DAG: %[[#bool:]] = OpTypeBool
+; CHECK-DAG: %[[#scope:]] = OpConstant %[[#uint]] 3
+
+; CHECK-LABEL: Begin function test_float
+; CHECK: %[[#fexpr:]] = OpFunctionParameter %[[#f32]]
+define internal float @test_float(float %fexpr) {
+entry:
+; CHECK: %[[#]] = OpGroupNonUniformBroadcastFirst %[[#f32]] %[[#scope]] %[[#fexpr]]
+  %0 = call float @llvm.spv.wave.readlane.first.f32(float %fexpr)
+  ret float %0
+}
+
+; CHECK-LABEL: Begin function test_int
+; CHECK: %[[#iexpr:]] = OpFunctionParameter %[[#uint]]
+define internal i32 @test_int(i32 %iexpr) {
+entry:
+; CHECK: %[[#]] = OpGroupNonUniformBroadcastFirst %[[#uint]] %[[#scope]] %[[#iexpr]]
+  %0 = call i32 @llvm.spv.wave.readlane.first.i32(i32 %iexpr)
+  ret i32 %0
+}
+
+; CHECK-LABEL: Begin function test_bool
+; CHECK: %[[#bexpr:]] = OpFunctionParameter %[[#bool]]
+define internal i1 @test_bool(i1 %bexpr) {
+entry:
+; CHECK: %[[#]] = OpGroupNonUniformBroadcastFirst %[[#bool]] %[[#scope]] %[[#bexpr]]
+  %0 = call i1 @llvm.spv.wave.readlane.first.i1(i1 %bexpr)
+  ret i1 %0
+}
+
+; CHECK-LABEL: Begin function test_vfloat
+; CHECK: %[[#vfexpr:]] = OpFunctionParameter %[[#v4_float]]
+define internal <4 x float> @test_vfloat(<4 x float> %vfexpr) {
+entry:
+; CHECK: %[[#]] = OpGroupNonUniformBroadcastFirst %[[#v4_float]] %[[#scope]] %[[#vfexpr]]
+  %0 = call <4 x float> @llvm.spv.wave.readlane.first.v4f32(
+      <4 x float> %vfexpr)
+  ret <4 x float> %0
+}
+
+define void @main() #0 {
+  ret void
+}
+
+declare float @llvm.spv.wave.readlane.first.f32(float)
+declare i32 @llvm.spv.wave.readlane.first.i32(i32)
+declare i1 @llvm.spv.wave.readlane.first.i1(i1)
+declare <4 x float> @llvm.spv.wave.readlane.first.v4f32(<4 x float>)
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }

diff  --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst_mat.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst_mat.ll
new file mode 100644
index 0000000000000..02517fb657171
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/WaveReadLaneFirst_mat.ll
@@ -0,0 +1,54 @@
+; XFAIL: *
+; TODO: Support matrix legalization for SPIR-V target intrinsics.
+; https://github.com/llvm/llvm-project/issues/225961
+;
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-compute %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %}
+
+; Test WaveReadLaneFirst lowering for matrix types without long vectors.
+
+; CHECK: Capability Shader
+; CHECK: Capability GroupNonUniformBallot
+; CHECK-NOT: Capability LongVectorEXT
+; CHECK-NOT: Extension "SPV_EXT_long_vector"
+
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#f32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#v2_float:]] = OpTypeVector %[[#f32]] 2
+; CHECK-DAG: %[[#v4_float:]] = OpTypeVector %[[#f32]] 4
+; CHECK-DAG: %[[#scope:]] = OpConstant %[[#uint]] 3
+
+ at wide_f32_6 = internal addrspace(10) global [6 x float] zeroinitializer
+ at wide_f32_12 = internal addrspace(10) global [12 x float] zeroinitializer
+
+; CHECK-LABEL: Begin function test_float2x3
+define internal void @test_float2x3() {
+entry:
+  %expr = load <6 x float>, ptr addrspace(10) @wide_f32_6
+; CHECK: OpGroupNonUniformBroadcastFirst %[[#v4_float]] %[[#scope]]
+; CHECK: OpGroupNonUniformBroadcastFirst %[[#v2_float]] %[[#scope]]
+  %result = call <6 x float> @llvm.spv.wave.readlane.first.v6f32(
+      <6 x float> %expr)
+  store <6 x float> %result, ptr addrspace(10) @wide_f32_6
+  ret void
+}
+
+; CHECK-LABEL: Begin function test_float3x4
+define internal void @test_float3x4() {
+entry:
+  %expr = load <12 x float>, ptr addrspace(10) @wide_f32_12
+; CHECK-COUNT-3: OpGroupNonUniformBroadcastFirst %[[#v4_float]] %[[#scope]]
+  %result = call <12 x float> @llvm.spv.wave.readlane.first.v12f32(
+      <12 x float> %expr)
+  store <12 x float> %result, ptr addrspace(10) @wide_f32_12
+  ret void
+}
+
+define void @main() #0 {
+  ret void
+}
+
+declare <6 x float> @llvm.spv.wave.readlane.first.v6f32(<6 x float>)
+declare <12 x float> @llvm.spv.wave.readlane.first.v12f32(<12 x float>)
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }


        


More information about the llvm-commits mailing list