-
Notifications
You must be signed in to change notification settings - Fork 13.8k
[HLSL] Implement WaveReadLaneAt
intrinsic
#111010
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7008964
e8c5dba
1b0746f
473150d
484b208
dc3fc0b
5a2c594
32f1015
906f105
1fc05b6
f181e27
49edfec
dd73a1e
8a75558
78f7e5d
087191d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -fnative-half-type -triple \ | ||
// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -disable-llvm-passes -o - | \ | ||
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-DXIL | ||
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -fnative-half-type -triple \ | ||
// RUN: spirv-pc-vulkan-compute %s -emit-llvm -disable-llvm-passes -o - | \ | ||
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV | ||
|
||
// Test basic lowering to runtime function call for int values. | ||
|
||
// CHECK-LABEL: test_int | ||
int test_int(int expr, uint idx) { | ||
// CHECK-SPIRV: %[[#entry_tok0:]] = call token @llvm.experimental.convergence.entry() | ||
// CHECK-SPIRV: %[[RET:.*]] = call [[TY:.*]] @llvm.spv.wave.readlane.i32([[TY]] %[[#]], i32 %[[#]]) [ "convergencectrl"(token %[[#entry_tok0]]) ] | ||
// CHECK-DXIL: %[[RET:.*]] = call [[TY:.*]] @llvm.dx.wave.readlane.i32([[TY]] %[[#]], i32 %[[#]]) | ||
// CHECK: ret [[TY]] %[[RET]] | ||
return WaveReadLaneAt(expr, idx); | ||
} | ||
|
||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.i32([[TY]], i32) #[[#attr:]] | ||
// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.i32([[TY]], i32) #[[#attr:]] | ||
|
||
#ifdef __HLSL_ENABLE_16_BIT | ||
// CHECK-LABEL: test_int16 | ||
int16_t test_int16(int16_t expr, uint idx) { | ||
// CHECK-SPIRV: %[[#entry_tok1:]] = call token @llvm.experimental.convergence.entry() | ||
// CHECK-SPIRV: %[[RET:.*]] = call [[TY:.*]] @llvm.spv.wave.readlane.i16([[TY]] %[[#]], i32 %[[#]]) [ "convergencectrl"(token %[[#entry_tok1]]) ] | ||
// CHECK-DXIL: %[[RET:.*]] = call [[TY:.*]] @llvm.dx.wave.readlane.i16([[TY]] %[[#]], i32 %[[#]]) | ||
// CHECK: ret [[TY]] %[[RET]] | ||
return WaveReadLaneAt(expr, idx); | ||
} | ||
|
||
// CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.i16([[TY]], i32) #[[#attr:]] | ||
// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.i16([[TY]], i32) #[[#attr:]] | ||
#endif | ||
|
||
// Test basic lowering to runtime function call with array and float values. | ||
|
||
// CHECK-LABEL: test_half | ||
half test_half(half expr, uint idx) { | ||
// CHECK-SPIRV: %[[#entry_tok2:]] = call token @llvm.experimental.convergence.entry() | ||
// CHECK-SPIRV: %[[RET:.*]] = call [[TY:.*]] @llvm.spv.wave.readlane.f16([[TY]] %[[#]], i32 %[[#]]) [ "convergencectrl"(token %[[#entry_tok2]]) ] | ||
// CHECK-DXIL: %[[RET:.*]] = call [[TY:.*]] @llvm.dx.wave.readlane.f16([[TY]] %[[#]], i32 %[[#]]) | ||
// CHECK: ret [[TY]] %[[RET]] | ||
return WaveReadLaneAt(expr, idx); | ||
} | ||
|
||
// CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.f16([[TY]], i32) #[[#attr:]] | ||
// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.f16([[TY]], i32) #[[#attr:]] | ||
|
||
// CHECK-LABEL: test_double | ||
double test_double(double expr, uint idx) { | ||
// CHECK-SPIRV: %[[#entry_tok3:]] = call token @llvm.experimental.convergence.entry() | ||
// CHECK-SPIRV: %[[RET:.*]] = call [[TY:.*]] @llvm.spv.wave.readlane.f64([[TY]] %[[#]], i32 %[[#]]) [ "convergencectrl"(token %[[#entry_tok3]]) ] | ||
// CHECK-DXIL: %[[RET:.*]] = call [[TY:.*]] @llvm.dx.wave.readlane.f64([[TY]] %[[#]], i32 %[[#]]) | ||
// CHECK: ret [[TY]] %[[RET]] | ||
return WaveReadLaneAt(expr, idx); | ||
} | ||
|
||
// CHECK-DXIL: declare [[TY]] @llvm.dx.wave.readlane.f64([[TY]], i32) #[[#attr:]] | ||
// CHECK-SPIRV: declare [[TY]] @llvm.spv.wave.readlane.f64([[TY]], i32) #[[#attr:]] | ||
|
||
// CHECK-LABEL: test_floatv4 | ||
float4 test_floatv4(float4 expr, uint idx) { | ||
// CHECK-SPIRV: %[[#entry_tok4:]] = call token @llvm.experimental.convergence.entry() | ||
// CHECK-SPIRV: %[[RET1:.*]] = call [[TY1:.*]] @llvm.spv.wave.readlane.v4f32([[TY1]] %[[#]], i32 %[[#]]) [ "convergencectrl"(token %[[#entry_tok4]]) ] | ||
// CHECK-DXIL: %[[RET1:.*]] = call [[TY1:.*]] @llvm.dx.wave.readlane.v4f32([[TY1]] %[[#]], i32 %[[#]]) | ||
// CHECK: ret [[TY1]] %[[RET1]] | ||
return WaveReadLaneAt(expr, idx); | ||
} | ||
|
||
// CHECK-DXIL: declare [[TY1]] @llvm.dx.wave.readlane.v4f32([[TY1]], i32) #[[#attr]] | ||
// CHECK-SPIRV: declare [[TY1]] @llvm.spv.wave.readlane.v4f32([[TY1]], i32) #[[#attr]] | ||
|
||
// CHECK: attributes #[[#attr]] = {{{.*}} convergent {{.*}}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// 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_at(); | ||
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}} | ||
} | ||
|
||
float2 test_too_few_arg_1(float2 p0) { | ||
return __builtin_hlsl_wave_read_lane_at(p0); | ||
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}} | ||
} | ||
|
||
float2 test_too_many_arg(float2 p0) { | ||
return __builtin_hlsl_wave_read_lane_at(p0, p0, p0); | ||
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}} | ||
} | ||
|
||
float3 test_index_double_type_check(float3 p0, double idx) { | ||
return __builtin_hlsl_wave_read_lane_at(p0, idx); | ||
// expected-error@-1 {{passing 'double' to parameter of incompatible type 'unsigned int'}} | ||
} | ||
|
||
float3 test_index_int3_type_check(float3 p0, int3 idxs) { | ||
return __builtin_hlsl_wave_read_lane_at(p0, idxs); | ||
// expected-error@-1 {{passing 'int3' (aka 'vector<int, 3>') to parameter of incompatible type 'unsigned int'}} | ||
} | ||
|
||
struct S { float f; }; | ||
|
||
float3 test_index_S_type_check(float3 p0, S idx) { | ||
return __builtin_hlsl_wave_read_lane_at(p0, idx); | ||
// expected-error@-1 {{passing 'S' to parameter of incompatible type 'unsigned int'}} | ||
} | ||
|
||
S test_expr_struct_type_check(S p0, int idx) { | ||
return __builtin_hlsl_wave_read_lane_at(p0, idx); | ||
// expected-error@-1 {{invalid operand of type 'S' where a scalar or vector is required}} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,6 +230,9 @@ class SPIRVInstructionSelector : public InstructionSelector { | |
bool selectSpvThreadId(Register ResVReg, const SPIRVType *ResType, | ||
MachineInstr &I) const; | ||
|
||
bool selectWaveReadLaneAt(Register ResVReg, const SPIRVType *ResType, | ||
MachineInstr &I) const; | ||
|
||
bool selectUnmergeValues(MachineInstr &I) const; | ||
|
||
void selectHandleFromBinding(Register &ResVReg, const SPIRVType *ResType, | ||
|
@@ -417,6 +420,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg, | |
|
||
case TargetOpcode::G_INTRINSIC: | ||
case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: | ||
case TargetOpcode::G_INTRINSIC_CONVERGENT: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there anything that distinguishes a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Afaict, not within this section of the codebase, it is used elsewhere though eg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The extra convergence information is being used when we generate the merge instructions. They should not change how the instrinsic itself is generated. The convergence token help us distinguish between:
|
||
case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: | ||
return selectIntrinsic(ResVReg, ResType, I); | ||
case TargetOpcode::G_BITREVERSE: | ||
|
@@ -1758,6 +1762,26 @@ bool SPIRVInstructionSelector::selectSign(Register ResVReg, | |
return Result; | ||
} | ||
|
||
bool SPIRVInstructionSelector::selectWaveReadLaneAt(Register ResVReg, | ||
const SPIRVType *ResType, | ||
MachineInstr &I) const { | ||
assert(I.getNumOperands() == 4); | ||
assert(I.getOperand(2).isReg()); | ||
assert(I.getOperand(3).isReg()); | ||
MachineBasicBlock &BB = *I.getParent(); | ||
|
||
// IntTy is used to define the execution scope, set to 3 to denote a | ||
// cross-lane interaction equivalent to a SPIR-V subgroup. | ||
SPIRVType *IntTy = GR.getOrCreateSPIRVIntegerType(32, I, TII); | ||
return BuildMI(BB, I, I.getDebugLoc(), | ||
TII.get(SPIRV::OpGroupNonUniformShuffle)) | ||
.addDef(ResVReg) | ||
.addUse(GR.getSPIRVTypeID(ResType)) | ||
.addUse(GR.getOrCreateConstInt(3, I, IntTy, TII)) | ||
.addUse(I.getOperand(2).getReg()) | ||
.addUse(I.getOperand(3).getReg()); | ||
} | ||
|
||
bool SPIRVInstructionSelector::selectBitreverse(Register ResVReg, | ||
const SPIRVType *ResType, | ||
MachineInstr &I) const { | ||
|
@@ -2541,6 +2565,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, | |
.addUse(GR.getSPIRVTypeID(ResType)) | ||
.addUse(GR.getOrCreateConstInt(3, I, IntTy, TII)); | ||
} | ||
case Intrinsic::spv_wave_readlane: | ||
return selectWaveReadLaneAt(ResVReg, ResType, I); | ||
case Intrinsic::spv_step: | ||
return selectExtInst(ResVReg, ResType, I, CL::step, GL::Step); | ||
case Intrinsic::spv_radians: | ||
|
Uh oh!
There was an error while loading. Please reload this page.