Skip to content

Commit bc6c068

Browse files
[HLSL] Adding HLSL clip function. (#114588)
Adding HLSL `clip` function. - adding llvm intrinsic - adding sema checks - adding dxil lowering - ading spirv lowering - adding sema tests - adding codegen tests - adding lowering tests Closes #99093 --------- Co-authored-by: Joao Saffran <[email protected]>
1 parent 942928f commit bc6c068

File tree

18 files changed

+321
-1
lines changed

18 files changed

+321
-1
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4895,6 +4895,12 @@ def HLSLSplitDouble: LangBuiltin<"HLSL_LANG"> {
48954895
let Prototype = "void(...)";
48964896
}
48974897

4898+
def HLSLClip: LangBuiltin<"HLSL_LANG"> {
4899+
let Spellings = ["__builtin_hlsl_elementwise_clip"];
4900+
let Attributes = [NoThrow, Const];
4901+
let Prototype = "void(...)";
4902+
}
4903+
48984904
// Builtins for XRay.
48994905
def XRayCustomEvent : Builtin {
49004906
let Spellings = ["__xray_customevent"];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,47 @@ static void initializeAlloca(CodeGenFunction &CGF, AllocaInst *AI, Value *Size,
9999
I->addAnnotationMetadata("auto-init");
100100
}
101101

102+
static Value *handleHlslClip(const CallExpr *E, CodeGenFunction *CGF) {
103+
Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
104+
105+
Constant *FZeroConst = ConstantFP::getZero(CGF->FloatTy);
106+
Value *CMP;
107+
Value *LastInstr;
108+
109+
if (const auto *VecTy = E->getArg(0)->getType()->getAs<clang::VectorType>()) {
110+
FZeroConst = ConstantVector::getSplat(
111+
ElementCount::getFixed(VecTy->getNumElements()), FZeroConst);
112+
auto *FCompInst = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
113+
CMP = CGF->Builder.CreateIntrinsic(
114+
CGF->Builder.getInt1Ty(), CGF->CGM.getHLSLRuntime().getAnyIntrinsic(),
115+
{FCompInst}, nullptr);
116+
} else
117+
CMP = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
118+
119+
if (CGF->CGM.getTarget().getTriple().isDXIL())
120+
LastInstr = CGF->Builder.CreateIntrinsic(
121+
CGF->VoidTy, llvm::Intrinsic::dx_discard, {CMP}, nullptr);
122+
else if (CGF->CGM.getTarget().getTriple().isSPIRV()) {
123+
BasicBlock *LT0 = CGF->createBasicBlock("lt0", CGF->CurFn);
124+
BasicBlock *End = CGF->createBasicBlock("end", CGF->CurFn);
125+
126+
CGF->Builder.CreateCondBr(CMP, LT0, End);
127+
128+
CGF->Builder.SetInsertPoint(LT0);
129+
130+
CGF->Builder.CreateIntrinsic(CGF->VoidTy, llvm::Intrinsic::spv_discard, {},
131+
nullptr);
132+
133+
LastInstr = CGF->Builder.CreateBr(End);
134+
135+
CGF->Builder.SetInsertPoint(End);
136+
} else {
137+
llvm_unreachable("Backend Codegen not supported.");
138+
}
139+
140+
return LastInstr;
141+
}
142+
102143
static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) {
103144
Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
104145
const auto *OutArg1 = dyn_cast<HLSLOutArgExpr>(E->getArg(1));
@@ -19208,6 +19249,10 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
1920819249
"asuint operands types mismatch");
1920919250
return handleHlslSplitdouble(E, this);
1921019251
}
19252+
case Builtin::BI__builtin_hlsl_elementwise_clip:
19253+
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
19254+
"clip operands types mismatch");
19255+
return handleHlslClip(E, this);
1921119256
}
1921219257
return nullptr;
1921319258
}

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,23 @@ double3 clamp(double3, double3, double3);
655655
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clamp)
656656
double4 clamp(double4, double4, double4);
657657

658+
//===----------------------------------------------------------------------===//
659+
// clip builtins
660+
//===----------------------------------------------------------------------===//
661+
662+
/// \fn void clip(T Val)
663+
/// \brief Discards the current pixel if the specified value is less than zero.
664+
/// \param Val The input value.
665+
666+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
667+
void clip(float);
668+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
669+
void clip(float2);
670+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
671+
void clip(float3);
672+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_clip)
673+
void clip(float4);
674+
658675
//===----------------------------------------------------------------------===//
659676
// cos builtins
660677
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,6 +2133,14 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
21332133
return true;
21342134
break;
21352135
}
2136+
case Builtin::BI__builtin_hlsl_elementwise_clip: {
2137+
if (SemaRef.checkArgCount(TheCall, 1))
2138+
return true;
2139+
2140+
if (CheckScalarOrVector(&SemaRef, TheCall, SemaRef.Context.FloatTy, 0))
2141+
return true;
2142+
break;
2143+
}
21362144
case Builtin::BI__builtin_elementwise_acos:
21372145
case Builtin::BI__builtin_elementwise_asin:
21382146
case Builtin::BI__builtin_elementwise_atan:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-pixel %s -fnative-half-type -emit-llvm -o - | FileCheck %s
2+
// RUN: %clang_cc1 -finclude-default-header -triple spirv-vulkan-pixel %s -fnative-half-type -emit-llvm -o - | FileCheck %s --check-prefix=SPIRV
3+
4+
5+
void test_scalar(float Buf) {
6+
// CHECK: define void @{{.*}}test_scalar{{.*}}(float {{.*}} [[VALP:%.*]])
7+
// CHECK: [[LOAD:%.*]] = load float, ptr [[VALP]].addr, align 4
8+
// CHECK-NEXT: [[FCMP:%.*]] = fcmp olt float [[LOAD]], 0.000000e+00
9+
// CHECK-NO: call i1 @llvm.dx.any
10+
// CHECK-NEXT: call void @llvm.dx.discard(i1 [[FCMP]])
11+
//
12+
// SPIRV: define spir_func void @{{.*}}test_scalar{{.*}}(float {{.*}} [[VALP:%.*]])
13+
// SPIRV: [[LOAD:%.*]] = load float, ptr [[VALP]].addr, align 4
14+
// SPIRV-NEXT: [[FCMP:%.*]] = fcmp olt float [[LOAD]], 0.000000e+00
15+
// SPIRV-NO: call i1 @llvm.spv.any
16+
// SPIRV-NEXT: br i1 [[FCMP]], label %[[LTL:.*]], label %[[ENDL:.*]]
17+
// SPIRV: [[LTL]]: ; preds = %entry
18+
// SPIRV-NEXT: call void @llvm.spv.discard()
19+
// SPIRV: br label %[[ENDL]]
20+
clip(Buf);
21+
}
22+
23+
void test_vector4(float4 Buf) {
24+
// CHECK: define void @{{.*}}test_vector{{.*}}(<4 x float> {{.*}} [[VALP:%.*]])
25+
// CHECK: [[LOAD:%.*]] = load <4 x float>, ptr [[VALP]].addr, align 16
26+
// CHECK-NEXT: [[FCMP:%.*]] = fcmp olt <4 x float> [[LOAD]], zeroinitializer
27+
// CHECK-NEXT: [[ANYC:%.*]] = call i1 @llvm.dx.any.v4i1(<4 x i1> [[FCMP]])
28+
// CHECK-NEXT: call void @llvm.dx.discard(i1 [[ANYC]])
29+
//
30+
// SPIRV: define spir_func void @{{.*}}test_vector{{.*}}(<4 x float> {{.*}} [[VALP:%.*]])
31+
// SPIRV: [[LOAD:%.*]] = load <4 x float>, ptr [[VALP]].addr, align 16
32+
// SPIRV-NEXT: [[FCMP:%.*]] = fcmp olt <4 x float> [[LOAD]], zeroinitializer
33+
// SPIRV-NEXT: [[ANYC:%.*]] = call i1 @llvm.spv.any.v4i1(<4 x i1> [[FCMP]])
34+
// SPIRV-NEXT: br i1 [[ANYC]], label %[[LTL:.*]], label %[[ENDL:.*]]
35+
// SPIRV: [[LTL]]: ; preds = %entry
36+
// SPIRV-NEXT: call void @llvm.spv.discard()
37+
// SPIRV-NEXT: br label %[[ENDL]]
38+
clip(Buf);
39+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -verify
2+
3+
4+
void test_arg_missing() {
5+
__builtin_hlsl_elementwise_clip();
6+
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
7+
}
8+
9+
void test_too_many_args(float p1, float p2) {
10+
__builtin_hlsl_elementwise_clip(p1, p2);
11+
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
12+
}
13+
14+
void test_first_arg_type_mismatch(bool p) {
15+
__builtin_hlsl_elementwise_clip(p);
16+
// expected-error@-1 {{invalid operand of type 'bool' where 'float' or a vector of such type is required}}
17+
}
18+
19+
void test_first_arg_type_mismatch_3(half3 p) {
20+
__builtin_hlsl_elementwise_clip(p);
21+
// expected-error@-1 {{invalid operand of type 'half3' (aka 'vector<half, 3>') where 'float' or a vector of such type is required}}
22+
}
23+
24+
void test_first_arg_type_mismatch_3(double p) {
25+
__builtin_hlsl_elementwise_clip(p);
26+
// expected-error@-1 {{invalid operand of type 'double' where 'float' or a vector of such type is required}}
27+
}

llvm/docs/SPIRVUsage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
149149
- Adds atomic min and max instruction on floating-point numbers.
150150
* - ``SPV_EXT_arithmetic_fence``
151151
- Adds an instruction that prevents fast-math optimizations between its argument and the expression that contains it.
152+
* - ``SPV_EXT_demote_to_helper_invocation``
153+
- Adds an instruction that demotes a fragment shader invocation to a helper invocation.
152154
* - ``SPV_INTEL_arbitrary_precision_integers``
153155
- Allows generating arbitrary width integer types.
154156
* - ``SPV_INTEL_bfloat16_conversion``

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def int_dx_step : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, L
102102
def int_dx_splitdouble : DefaultAttrsIntrinsic<[llvm_anyint_ty, LLVMMatchType<0>],
103103
[LLVMScalarOrSameVectorWidth<0, llvm_double_ty>], [IntrNoMem]>;
104104
def int_dx_radians : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], [IntrNoMem]>;
105+
def int_dx_discard : DefaultAttrsIntrinsic<[], [llvm_i1_ty], []>;
105106
def int_dx_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
106107
def int_dx_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
107108
}

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ let TargetPrefix = "spv" in {
9191
def int_spv_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty], [IntrNoMem]>;
9292
def int_spv_radians : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty], [IntrNoMem]>;
9393
def int_spv_group_memory_barrier_with_group_sync : DefaultAttrsIntrinsic<[], [], []>;
94+
def int_spv_discard : DefaultAttrsIntrinsic<[], [], []>;
9495
def int_spv_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
9596
def int_spv_sclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
9697
def int_spv_nclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;

llvm/lib/Target/DirectX/DXIL.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,14 @@ def CheckAccessFullyMapped : DXILOp<71, checkAccessFullyMapped> {
770770
let stages = [Stages<DXIL1_0, [all_stages]>];
771771
}
772772

773+
def Discard : DXILOp<82, discard> {
774+
let Doc = "discard the current pixel";
775+
let LLVMIntrinsic = int_dx_discard;
776+
let arguments = [Int1Ty];
777+
let result = VoidTy;
778+
let stages = [Stages<DXIL1_0, [pixel]>];
779+
}
780+
773781
def ThreadId : DXILOp<93, threadId> {
774782
let Doc = "Reads the thread ID";
775783
let LLVMIntrinsic = int_dx_thread_id;

0 commit comments

Comments
 (0)