[HLSL][DXIL] Implement WaveGetLaneIndex Intrinsic (#111576)

- add additional lowering for directx backend in CGBuiltin.cpp
    - add directx intrinsic to IntrinsicsDirectX.td
    - add semantic check of arguments in SemaHLSL.cpp
    - add mapping to DXIL op in DXIL.td

    - add testing of semantics in WaveGetLaneIndex-errors.hlsl
    - add testing of dxil lowering in WaveGetLaneIndex.ll
  
Resolves #70105
This commit is contained in:
Finn Plummer
2024-10-10 11:44:44 -07:00
committed by GitHub
parent ba530e6b64
commit d36cef0b17
7 changed files with 60 additions and 9 deletions

View File

@@ -18867,9 +18867,21 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.step");
}
case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
llvm::FunctionType::get(IntTy, {}, false), "__hlsl_wave_get_lane_index",
{}, false, true));
// We don't define a SPIR-V intrinsic, instead it is a SPIR-V built-in
// defined in SPIRVBuiltins.td. So instead we manually get the matching name
// for the DirectX intrinsic and the demangled builtin name
switch (CGM.getTarget().getTriple().getArch()) {
case llvm::Triple::dxil:
return EmitRuntimeCall(Intrinsic::getDeclaration(
&CGM.getModule(), Intrinsic::dx_wave_getlaneindex));
case llvm::Triple::spirv:
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
llvm::FunctionType::get(IntTy, {}, false),
"__hlsl_wave_get_lane_index", {}, false, true));
default:
llvm_unreachable(
"Intrinsic WaveGetLaneIndex not supported by target architecture");
}
}
case Builtin::BI__builtin_hlsl_wave_is_first_lane: {
Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic();

View File

@@ -1992,6 +1992,11 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
if (SemaRef.checkArgCount(TheCall, 0))
return true;
break;
}
case Builtin::BI__builtin_elementwise_acos:
case Builtin::BI__builtin_elementwise_asin:
case Builtin::BI__builtin_elementwise_atan:

View File

@@ -1,14 +1,22 @@
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
// RUN: spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-SPIRV
// RUN: %clang_cc1 -finclude-default-header \
// RUN: -triple dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=CHECK,CHECK-DXIL
// CHECK: define spir_func noundef i32 @_Z6test_1v() [[A0:#[0-9]+]] {
// CHECK: %[[CI:[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK: call i32 @__hlsl_wave_get_lane_index() [ "convergencectrl"(token %[[CI]]) ]
uint test_1() {
// CHECK-SPIRV: define spir_func noundef i32 @{{.*test_1.*}}() [[A0:#[0-9]+]] {
// CHECK-DXIL: define noundef i32 @{{.*test_1.*}}() [[A0:#[0-9]+]] {
// CHECK-SPIRV: %[[CI:[0-9]+]] = call token @llvm.experimental.convergence.entry()
// CHECK-SPIRV: call i32 @__hlsl_wave_get_lane_index() [ "convergencectrl"(token %[[CI]]) ]
// CHECK-DXIL: call i32 @llvm.dx.wave.getlaneindex()
int test_1() {
return WaveGetLaneIndex();
}
// CHECK: declare i32 @__hlsl_wave_get_lane_index() [[A1:#[0-9]+]]
// CHECK-SPIRV: declare i32 @__hlsl_wave_get_lane_index() [[A1:#[0-9]+]]
// CHECK-DXIL: declare i32 @llvm.dx.wave.getlaneindex() [[A1:#[0-9]+]]
// CHECK-DAG: attributes [[A0]] = { {{.*}}convergent{{.*}} }
// CHECK-DAG: attributes [[A1]] = { {{.*}}convergent{{.*}} }

View File

@@ -0,0 +1,6 @@
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify
int test_too_many_arg(int x) {
return __builtin_hlsl_wave_get_lane_index(x);
// expected-error@-1 {{too many arguments to function call, expected 0, have 1}}
}

View File

@@ -83,6 +83,7 @@ def int_dx_imad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLV
def int_dx_umad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
def int_dx_normalize : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
def int_dx_rsqrt : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
def int_dx_wave_getlaneindex : DefaultAttrsIntrinsic<[llvm_i32_ty], [], [IntrConvergent, IntrNoMem]>;
def int_dx_wave_is_first_lane : DefaultAttrsIntrinsic<[llvm_i1_ty], [], [IntrConvergent]>;
def int_dx_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty], [IntrNoMem]>;
def int_dx_step : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>], [IntrNoMem]>;

View File

@@ -801,3 +801,12 @@ def WaveIsFirstLane : DXILOp<110, waveIsFirstLane> {
let stages = [Stages<DXIL1_0, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
def WaveGetLaneIndex : DXILOp<111, waveGetLaneIndex> {
let Doc = "returns the index of the current lane in the wave";
let LLVMIntrinsic = int_dx_wave_getlaneindex;
let arguments = [];
let result = Int32Ty;
let stages = [Stages<DXIL1_0, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

View File

@@ -0,0 +1,10 @@
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-compute %s | FileCheck %s
define void @main() {
entry:
; CHECK: call i32 @dx.op.waveGetLaneIndex(i32 111)
%0 = call i32 @llvm.dx.wave.getlaneindex()
ret void
}
declare i32 @llvm.dx.wave.getlaneindex()