-
Notifications
You must be signed in to change notification settings - Fork 11.9k
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
[HLSL] Adding HLSL clip
function.
#114588
base: main
Are you sure you want to change the base?
Conversation
You can test this locally with the following command:git-clang-format --diff a6637ae2cc9a0e7c9a37603b3d277d7ca642bc36 61c626de4c4d7315795545887e028f89116b054d --extensions cpp,h -- clang/lib/CodeGen/CGBuiltin.cpp clang/lib/Headers/hlsl/hlsl_intrinsics.h clang/lib/Sema/SemaHLSL.cpp llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp View the diff from clang-format here.diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 110faae3dc..b50fb4ba50 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -1408,14 +1408,15 @@ void addInstrRequirements(const MachineInstr &MI,
}
break;
case SPIRV::OpKill: {
- Reqs.addCapability(SPIRV::Capability::Shader) ;
+ Reqs.addCapability(SPIRV::Capability::Shader);
} break;
case SPIRV::OpDemoteToHelperInvocation:
if (ST.canUseExtension(
SPIRV::Extension::SPV_EXT_demote_to_helper_invocation)) {
Reqs.addExtension(SPIRV::Extension::SPV_EXT_demote_to_helper_invocation);
Reqs.addCapability(SPIRV::Capability::DemoteToHelperInvocation);
- } break;
+ }
+ break;
case SPIRV::OpSDot:
case SPIRV::OpUDot:
AddDotProductRequirements(MI, Reqs, ST);
|
061e3f4
to
bfad38c
Compare
ed1275d
to
0e14607
Compare
ec42b5b
to
2fb4bf9
Compare
2fb4bf9
to
20587a9
Compare
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-backend-x86 Author: None (joaosaffran) ChangesAdding HLSL
Closes #99093 Patch is 22.88 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/114588.diff 16 Files Affected:
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 9bd67e0cefebc3..13ba369e323f72 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4877,6 +4877,12 @@ def HLSLSplitDouble: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLClip: LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_clip"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 34fedd67114751..8177b144639180 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -99,6 +99,42 @@ static void initializeAlloca(CodeGenFunction &CGF, AllocaInst *AI, Value *Size,
I->addAnnotationMetadata("auto-init");
}
+static Value *handleHlslClip(const CallExpr *E, CodeGenFunction *CGF) {
+ Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
+
+ Constant *FZeroConst = ConstantFP::getZero(CGF->FloatTy);
+ Value *CMP;
+
+ if (const auto *VecTy = E->getArg(0)->getType()->getAs<clang::VectorType>()) {
+ FZeroConst = ConstantVector::getSplat(
+ ElementCount::getFixed(VecTy->getNumElements()), FZeroConst);
+ auto *FCompInst = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
+ CMP = CGF->Builder.CreateIntrinsic(
+ CGF->Builder.getInt1Ty(), CGF->CGM.getHLSLRuntime().getAnyIntrinsic(),
+ {FCompInst}, nullptr);
+ } else
+ CMP = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
+
+ if (CGF->CGM.getTarget().getTriple().isDXIL())
+ return CGF->Builder.CreateIntrinsic(CGF->VoidTy, llvm::Intrinsic::dx_clip,
+ {CMP}, nullptr);
+
+ BasicBlock *LT0 = CGF->createBasicBlock("lt0", CGF->CurFn);
+ BasicBlock *End = CGF->createBasicBlock("end", CGF->CurFn);
+
+ CGF->Builder.CreateCondBr(CMP, LT0, End);
+
+ CGF->Builder.SetInsertPoint(LT0);
+
+ CGF->Builder.CreateIntrinsic(CGF->VoidTy, llvm::Intrinsic::spv_clip, {},
+ nullptr);
+
+ auto *BrCall = CGF->Builder.CreateBr(End);
+
+ CGF->Builder.SetInsertPoint(End);
+ return BrCall;
+}
+
static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) {
Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
const auto *OutArg1 = dyn_cast<HLSLOutArgExpr>(E->getArg(1));
@@ -19093,6 +19129,11 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
"asuint operands types mismatch");
return handleHlslSplitdouble(E, this);
}
+ case Builtin::BI__builtin_hlsl_elementwise_clip:
+
+ assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
+ "clip operands types mismatch");
+ return handleHlslClip(E, this);
}
return nullptr;
}
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index d9f3a17ea23d8e..424c2f7e7c23ee 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -655,6 +655,23 @@ double3 clamp(double3, double3, double3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
double4 clamp(double4, double4, double4);
+//===----------------------------------------------------------------------===//
+// clip builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn void clip(T Val)
+/// \brief Discards the current pixel if the specified value is less than zero.
+/// \param Val The input value.
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
+void clip(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
+void clip(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
+void clip(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
+void clip(float4);
+
//===----------------------------------------------------------------------===//
// cos builtins
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index a472538236e2d9..e360c54dc0760e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2110,6 +2110,14 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
+ case Builtin::BI__builtin_hlsl_elementwise_clip: {
+ if (SemaRef.checkArgCount(TheCall, 1))
+ return true;
+
+ if (CheckScalarOrVector(&SemaRef, TheCall, SemaRef.Context.FloatTy, 0))
+ return true;
+ break;
+ }
case Builtin::BI__builtin_elementwise_acos:
case Builtin::BI__builtin_elementwise_asin:
case Builtin::BI__builtin_elementwise_atan:
diff --git a/clang/test/CodeGenHLSL/builtins/clip.hlsl b/clang/test/CodeGenHLSL/builtins/clip.hlsl
new file mode 100644
index 00000000000000..81976839bbe7d9
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/clip.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-pixel %s -fnative-half-type -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple spirv-vulkan-pixel %s -fnative-half-type -emit-llvm -o - | FileCheck %s --check-prefix=SPIRV
+
+
+void test_scalar(float Buf) {
+ // CHECK: define void @{{.*}}test_scalar{{.*}}(float {{.*}} [[VALP:%.*]])
+ // CHECK: [[LOAD:%.*]] = load float, ptr [[VALP]].addr, align 4
+ // CHECK-NEXT: [[FCMP:%.*]] = fcmp olt float [[LOAD]], 0.000000e+00
+ // CHECK-NO: call i1 @llvm.dx.any
+ // CHECK-NEXT: call void @llvm.dx.clip(i1 [[FCMP]])
+ //
+ // SPIRV: define spir_func void @{{.*}}test_scalar{{.*}}(float {{.*}} [[VALP:%.*]])
+ // SPIRV: [[LOAD:%.*]] = load float, ptr [[VALP]].addr, align 4
+ // SPIRV-NEXT: [[FCMP:%.*]] = fcmp olt float [[LOAD]], 0.000000e+00
+ // SPIRV-NO: call i1 @llvm.dx.any
+ // SPIRV-NEXT: br i1 [[FCMP]], label %[[LTL:.*]], label %[[ENDL:.*]]
+ // SPIRV: [[LTL]]: ; preds = %entry
+ // SPIRV-NEXT: call void @llvm.spv.clip()
+ // SPIRV: br label %[[ENDL]]
+ clip(Buf);
+}
+
+void test_vector4(float4 Buf) {
+ // CHECK: define void @{{.*}}test_vector{{.*}}(<4 x float> {{.*}} [[VALP:%.*]])
+ // CHECK: [[LOAD:%.*]] = load <4 x float>, ptr [[VALP]].addr, align 16
+ // CHECK-NEXT: [[FCMP:%.*]] = fcmp olt <4 x float> [[LOAD]], zeroinitializer
+ // CHECK-NEXT: [[ANYC:%.*]] = call i1 @llvm.dx.any.v4i1(<4 x i1> [[FCMP]])
+ // CHECK-NEXT: call void @llvm.dx.clip(i1 [[ANYC]])
+ //
+ // SPIRV: define spir_func void @{{.*}}test_vector{{.*}}(<4 x float> {{.*}} [[VALP:%.*]])
+ // SPIRV: [[LOAD:%.*]] = load <4 x float>, ptr [[VALP]].addr, align 16
+ // SPIRV-NEXT: [[FCMP:%.*]] = fcmp olt <4 x float> [[LOAD]], zeroinitializer
+ // SPIRV-NEXT: [[ANYC:%.*]] = call i1 @llvm.spv.any.v4i1(<4 x i1> [[FCMP]])
+ // SPIRV-NEXT: br i1 [[ANYC]], label %[[LTL:.*]], label %[[ENDL:.*]]
+ // SPIRV: [[LTL]]: ; preds = %entry
+ // SPIRV-NEXT: call void @llvm.spv.clip()
+ // SPIRV-NEXT: br label %[[ENDL]]
+ clip(Buf);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/clip-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/clip-errors.hlsl
new file mode 100644
index 00000000000000..570b4bc18dcd4b
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/clip-errors.hlsl
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -verify
+
+
+void test_arg_missing() {
+ __builtin_hlsl_elementwise_clip();
+ // expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+void test_too_many_args(float p1, float p2) {
+ __builtin_hlsl_elementwise_clip(p1, p2);
+ // expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
+}
+
+void test_first_arg_type_mismatch(bool p) {
+ __builtin_hlsl_elementwise_clip(p);
+ // expected-error@-1 {{invalid operand of type 'bool' where 'float' or a vector of such type is required}}
+}
+
+void test_first_arg_type_mismatch_2(half p) {
+ __builtin_hlsl_elementwise_clip(p);
+ // expected-error@-1 {{invalid operand of type 'double' where 'float' or a vector of such type is required}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index e30d37f69f781e..0d8dc4ead02c8a 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -92,4 +92,5 @@ def int_dx_step : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, L
def int_dx_splitdouble : DefaultAttrsIntrinsic<[llvm_anyint_ty, LLVMMatchType<0>],
[LLVMScalarOrSameVectorWidth<0, llvm_double_ty>], [IntrNoMem]>;
def int_dx_radians : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
+def int_dx_clip : DefaultAttrsIntrinsic<[], [llvm_i1_ty], [IntrNoMem]>;
}
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index ddb47390537412..9d2f85315f63e0 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -88,6 +88,7 @@ let TargetPrefix = "spv" in {
def int_spv_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty], [IntrNoMem]>;
def int_spv_radians : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
def int_spv_group_memory_barrier_with_group_sync : DefaultAttrsIntrinsic<[], [], []>;
+ def int_spv_clip : Intrinsic<[], [], []>;
// Create resource handle given the binding information. Returns a
// type appropriate for the kind of resource given the set id, binding id,
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 1e8dc63ffa257e..94b1f6ff9cc088 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -739,6 +739,15 @@ def CheckAccessFullyMapped : DXILOp<71, checkAccessFullyMapped> {
let stages = [Stages<DXIL1_0, [all_stages]>];
}
+def Discard : DXILOp<82, discard> {
+ let Doc = "discard the current pixel";
+ let LLVMIntrinsic = int_dx_clip;
+ let arguments = [Int1Ty];
+ let result = VoidTy;
+ let stages = [Stages<DXIL1_0, [pixel]>];
+ let attributes = [Attributes<DXIL1_0, [ReadNone]>];
+}
+
def ThreadId : DXILOp<93, threadId> {
let Doc = "Reads the thread ID";
let LLVMIntrinsic = int_dx_thread_id;
@@ -788,20 +797,6 @@ def SplitDouble : DXILOp<102, splitDouble> {
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}
-def AnnotateHandle : DXILOp<217, annotateHandle> {
- let Doc = "annotate handle with resource properties";
- let arguments = [HandleTy, ResPropsTy];
- let result = HandleTy;
- let stages = [Stages<DXIL1_6, [all_stages]>];
-}
-
-def CreateHandleFromBinding : DXILOp<218, createHandleFromBinding> {
- let Doc = "create resource handle from binding";
- let arguments = [ResBindTy, Int32Ty, Int1Ty];
- let result = HandleTy;
- let stages = [Stages<DXIL1_6, [all_stages]>];
-}
-
def WaveIsFirstLane : DXILOp<110, waveIsFirstLane> {
let Doc = "returns 1 for the first lane in the wave";
let LLVMIntrinsic = int_dx_wave_is_first_lane;
@@ -811,6 +806,15 @@ def WaveIsFirstLane : DXILOp<110, waveIsFirstLane> {
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]>];
+}
+
def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
let Doc = "returns the value from the specified lane";
let LLVMIntrinsic = int_dx_wave_readlane;
@@ -821,11 +825,16 @@ def WaveReadLaneAt: DXILOp<117, waveReadLaneAt> {
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]>];
+def AnnotateHandle : DXILOp<217, annotateHandle> {
+ let Doc = "annotate handle with resource properties";
+ let arguments = [HandleTy, ResPropsTy];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
+}
+
+def CreateHandleFromBinding : DXILOp<218, createHandleFromBinding> {
+ let Doc = "create resource handle from binding";
+ let arguments = [ResBindTy, Int32Ty, Int1Ty];
+ let result = HandleTy;
+ let stages = [Stages<DXIL1_6, [all_stages]>];
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index dbfc133864bba4..23221cacca7df3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -73,7 +73,9 @@ static const std::map<std::string, SPIRV::Extension::Extension>
{"SPV_KHR_cooperative_matrix",
SPIRV::Extension::Extension::SPV_KHR_cooperative_matrix},
{"SPV_KHR_non_semantic_info",
- SPIRV::Extension::Extension::SPV_KHR_non_semantic_info}};
+ SPIRV::Extension::Extension::SPV_KHR_non_semantic_info},
+ {"SPV_EXT_demote_to_helper_invocation",
+ SPIRV::Extension::Extension::SPV_EXT_demote_to_helper_invocation}};
bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
llvm::StringRef ArgValue,
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index ee6b70a16417f4..d8e27153bd7d24 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -636,6 +636,7 @@ let isReturn = 1, hasDelaySlot = 0, isBarrier = 0, isTerminator = 1, isNotDuplic
}
def OpLifetimeStart: Op<256, (outs), (ins ID:$ptr, i32imm:$sz), "OpLifetimeStart $ptr, $sz">;
def OpLifetimeStop: Op<257, (outs), (ins ID:$ptr, i32imm:$sz), "OpLifetimeStop $ptr, $sz">;
+def OpDemoteToHelperInvocation: SimpleOp<"OpDemoteToHelperInvocation", 5380>;
// 3.42.18 Atomic Instructions
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 892912a5680113..be3852ed482c83 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -147,6 +147,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
unsigned comparisonOpcode, MachineInstr &I) const;
bool selectCross(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
+ bool selectClip(Register ResVReg, const SPIRVType *ResType,
+ MachineInstr &I) const;
+
bool selectICmp(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
bool selectFCmp(Register ResVReg, const SPIRVType *ResType,
@@ -1966,6 +1969,32 @@ bool SPIRVInstructionSelector::selectSplatVector(Register ResVReg,
return MIB.constrainAllUses(TII, TRI, RBI);
}
+bool SPIRVInstructionSelector::selectClip(Register ResVReg,
+ const SPIRVType *ResType,
+ MachineInstr &I) const {
+
+ unsigned Opcode;
+
+ if (STI.isAtLeastSPIRVVer(VersionTuple(1, 6))) {
+ if (!STI.canUseExtension(
+ SPIRV::Extension::SPV_EXT_demote_to_helper_invocation))
+ report_fatal_error(
+ "llvm.spv.clip intrinsic: this instruction requires the following "
+ "SPIR-V extension: SPV_EXT_demote_to_helper_invocation",
+ false);
+ Opcode = SPIRV::OpDemoteToHelperInvocation;
+ } else {
+ Opcode = SPIRV::OpKill;
+ // OpKill must be the last operation of any basic block.
+ MachineInstr *NextI = I.getNextNode();
+ NextI->removeFromParent();
+ }
+
+ MachineBasicBlock &BB = *I.getParent();
+ return BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode))
+ .constrainAllUses(TII, TRI, RBI);
+}
+
bool SPIRVInstructionSelector::selectCmp(Register ResVReg,
const SPIRVType *ResType,
unsigned CmpOpc,
@@ -2599,6 +2628,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
selectHandleFromBinding(ResVReg, ResType, I);
return true;
}
+ case Intrinsic::spv_clip: {
+ return selectClip(ResVReg, ResType, I);
+ }
default: {
std::string DiagMsg;
raw_string_ostream OS(DiagMsg);
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 29ce60d9983e38..bc41ee96f2af66 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -1376,6 +1376,17 @@ void addInstrRequirements(const MachineInstr &MI,
Reqs.addCapability(SPIRV::Capability::SplitBarrierINTEL);
}
break;
+
+ case SPIRV::OpKill: {
+ Reqs.addCapability(SPIRV::Capability::Shader);
+ } break;
+ case SPIRV::OpDemoteToHelperInvocation:
+ if (ST.canUseExtension(
+ SPIRV::Extension::SPV_EXT_demote_to_helper_invocation)) {
+ Reqs.addExtension(SPIRV::Extension::SPV_EXT_demote_to_helper_invocation);
+ Reqs.addCapability(SPIRV::Capability::DemoteToHelperInvocation);
+ }
+ break;
default:
break;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index d63438baca7e76..edf6e5547631a1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -456,6 +456,7 @@ defm VulkanMemoryModelDeviceScopeKHR : CapabilityOperand<5346, 0, 0, [], []>;
defm ImageFootprintNV : CapabilityOperand<5282, 0, 0, [], []>;
defm FragmentBarycentricNV : CapabilityOperand<5284, 0, 0, [], []>;
defm ComputeDerivativeGroupQuadsNV : CapabilityOperand<5288, 0, 0, [], []>;
+defm DemoteToHelperInvocation : CapabilityOperand<5379, 0, 0, [SPV_EXT_demote_to_helper_invocation], []>;
defm ComputeDerivativeGroupLinearNV : CapabilityOperand<5350, 0, 0, [], []>;
defm FragmentDensityEXT : CapabilityOperand<5291, 0, 0, [], [Shader]>;
defm PhysicalStorageBufferAddressesEXT : CapabilityOperand<5347, 0, 0, [], [Shader]>;
diff --git a/llvm/test/CodeGen/DirectX/clip.ll b/llvm/test/CodeGen/DirectX/clip.ll
new file mode 100644
index 00000000000000..71789e7048363a
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/clip.ll
@@ -0,0 +1,29 @@
+; RUN: opt -passes='function(scalarizer),module(dxil-op-lower,dxil-intrinsic-expansion)' -S -mtriple=dxil-pc-shadermodel6.3-pixel %s | FileCheck %s
+
+; CHECK-LABEL: define void @test_scalar
+; CHECK: call void @dx.op.discard(i32 82, i1 %0)
+;
+define void @test_scalar(float noundef %p) #0 {
+entry:
+ %0 = fcmp olt float %p, 0.000000e+00
+ call void @llvm.dx.clip(i1 %0)
+ ret void
+}
+
+; CHECK-LABEL: define void @test_vector
+; CHECK: [[EXTR0:%.*]] = extractelement <4 x i1> [[INP:%.*]], i64 0
+; CHECK-NEXT: [[EXTR1:%.*]] = extractelement <4 x i1> [[INP:%.*]], i64 1
+; CHECK-NEXT: [[OR1:%.*]] = or i1 [[EXTR0]], [[EXTR1]]
+; CHECK-NEXT: [[EXTR2:%.*]] = extractelement <4 x i1> [[INP:%.*]], i64 2
+; CHECK-NEXT: [[OR2:%.*]] = or i1 [[OR1]], [[EXTR2]]
+; CHECK-NEXT: [[EXTR3:%.*]] = extractelement <4 x i1> [[INP:%.*]], i64 3
+; CHECK-NEXT: [[OR3:%.*]] = or i1 [[OR2]], [[EXTR3]]
+; CHECK-NEXT: call void @dx.op.discard(i32 82, i1 [[OR3]])
+;
+define void @test_vector(<4 x float> noundef %p) #0 {
+entry:
+ %0 = fcmp olt <4 x float> %p, zeroinitializer
+ %1 = call i1 @llvm.dx.any.v4i1(<4 x i1> %0)
+ call void @llvm.dx.clip(i1 %1)
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clip.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clip.ll
new file mode 100644
index 00000000000000..89db4be3494ebd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/clip.ll
@@ -0,0 +1,77 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s --check-prefixes=CHECK,SPIRV15
+; RUN: llc -verify-machineinstrs -spirv-ext=+SPV_EXT_demote_to_helper_invocation -O0 -mtriple=spirv32v1.6-unknown-unknown %s -o - | FileCheck %s --check-prefixes=CHECK,SPIRV16
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+
+; Make sure lowering is ...
[truncated]
|
clang/lib/CodeGen/CGBuiltin.cpp
Outdated
CMP = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst); | ||
|
||
if (CGF->CGM.getTarget().getTriple().isDXIL()) | ||
return CGF->Builder.CreateIntrinsic(CGF->VoidTy, llvm::Intrinsic::dx_clip, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit different than how we have done intrinsics that invoke other intrinsics. For example see expandNormalizeIntrinsic
in llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
. In this example dx_normaize
exapnds to:
fdiv
dx_dot
dx_rsqrt
fmul
While in this case we associate the instruction expansion with the clang builtin. This isn't wrong, its just different as past implementers have tried to keep target specific behaviors in their respective targets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will be happy to move the expansion into llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
, in order to keep code consistency. However, I have a question, do we have a similar "expassion" pass for spirv? Asking because the any
intrinsic is used both by SPIRV and DXIL backend in order to handle the vector scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For SPIRV I beleive you could just invoke selectAny
in llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
. Also not saying this has to change. But I want to be conservative with adding new patterns unless there is a good reason to do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is an example of the current generated IR:
define void @test_vector(<4 x float> noundef %Buf) {
entry:
%Buf.addr = alloca <4 x float>, align 16
store <4 x float> %Buf, ptr %Buf.addr, align 16
%1 = load <4 x float>, ptr %Buf.addr, align 16
%2 = fcmp olt <4 x float> %1, zeroinitializer
%3 = call i1 @llvm.spv.any.v4i1(<4 x i1> %2)
br i1 %3, label %lt0, label %end
lt0: ; preds = %entry
call void @llvm.spv.clip()
br label %end
end: ; preds = %lt0, %entry
ret void
}
The challenge, I see, with invoke selectAny
when handling selectClip
is that we need to call any
before calling the conditional branch. In order to call selectAny
in llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
that would also require creating the branch call when selecting the spirv instruction. That doesn't seem to be the proper place to generate the branching code. Please, let me know if I am missing something here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now, you have @llvm.spv.clip()
maps to OpDemoteToHelperInvocation
or OpKill
depending on some extension availability. What I'm suggesting is twofold. First make your intrinsic the same number of arguments as your clang alias and the same as the dx intrinsic. Second that will mean register 1 to selectClip
is what you will be used by selectAny
Then do your branch invocations only in the spirv backend. In summary the instruction selection will be everything you need for this intrinsic not just OpDemoteToHelperInvocation or
OpKill`.
https://hlsl.godbolt.org/z/ose5EGWEx
%21 = OpFOrdLessThan %v4bool %20 %11
%22 = OpAny %bool %21
OpSelectionMerge %23 None
OpBranchConditional %22 %24 %23
%24 = OpLabel
OpKill | OpDemoteToHelperInvocation
%23 = OpLabel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This process make sense, however generating basic blocks doesn't seem to be allowed in SPIRV backend yet, as I see this error when creating new MBBs in llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
.
LLVM ERROR: inserting blocks is not supported yet (in function: test_scalar)
ddfc9c4
to
834ebff
Compare
834ebff
to
61c626d
Compare
Adding HLSL
clip
function.Closes #99093