Skip to content

Commit a5f501e

Browse files
authored
[HLSL][DXIL] Implement asdouble intrinsic (llvm#114847)
- define intrinsic as builtin in Builtins.td - link intrinsic in hlsl_intrinsics.h - add semantic analysis to SemaHLSL.cpp - lower to `llvm` or a `dx` intrinsic when applicable in CGBuiltin.cpp - define DXIL intrinsic in IntrinsicsDirectX.td - add DXIL op and mapping in DXIL.td - enable scalarization of intrinsic - add basic sema checking to asdouble-errors.hlsl Resolves llvm#99081
1 parent 689c532 commit a5f501e

File tree

10 files changed

+158
-0
lines changed

10 files changed

+158
-0
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4750,6 +4750,12 @@ def HLSLAny : LangBuiltin<"HLSL_LANG"> {
47504750
let Prototype = "bool(...)";
47514751
}
47524752

4753+
def HLSLAsDouble : LangBuiltin<"HLSL_LANG"> {
4754+
let Spellings = ["__builtin_hlsl_asdouble"];
4755+
let Attributes = [NoThrow, Const];
4756+
let Prototype = "void(...)";
4757+
}
4758+
47534759
def HLSLWaveActiveAnyTrue : LangBuiltin<"HLSL_LANG"> {
47544760
let Spellings = ["__builtin_hlsl_wave_active_any_true"];
47554761
let Attributes = [NoThrow, Const];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,41 @@ static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) {
209209
return LastInst;
210210
}
211211

212+
Value *handleAsDoubleBuiltin(CodeGenFunction &CGF, const CallExpr *E) {
213+
assert((E->getArg(0)->getType()->hasUnsignedIntegerRepresentation() &&
214+
E->getArg(1)->getType()->hasUnsignedIntegerRepresentation()) &&
215+
"asdouble operands types mismatch");
216+
Value *OpLowBits = CGF.EmitScalarExpr(E->getArg(0));
217+
Value *OpHighBits = CGF.EmitScalarExpr(E->getArg(1));
218+
219+
llvm::Type *ResultType = CGF.DoubleTy;
220+
int N = 1;
221+
if (auto *VTy = E->getArg(0)->getType()->getAs<clang::VectorType>()) {
222+
N = VTy->getNumElements();
223+
ResultType = llvm::FixedVectorType::get(CGF.DoubleTy, N);
224+
}
225+
226+
if (CGF.CGM.getTarget().getTriple().isDXIL())
227+
return CGF.Builder.CreateIntrinsic(
228+
/*ReturnType=*/ResultType, Intrinsic::dx_asdouble,
229+
ArrayRef<Value *>{OpLowBits, OpHighBits}, nullptr, "hlsl.asdouble");
230+
231+
if (!E->getArg(0)->getType()->isVectorType()) {
232+
OpLowBits = CGF.Builder.CreateVectorSplat(1, OpLowBits);
233+
OpHighBits = CGF.Builder.CreateVectorSplat(1, OpHighBits);
234+
}
235+
236+
llvm::SmallVector<int> Mask;
237+
for (int i = 0; i < N; i++) {
238+
Mask.push_back(i);
239+
Mask.push_back(i + N);
240+
}
241+
242+
Value *BitVec = CGF.Builder.CreateShuffleVector(OpLowBits, OpHighBits, Mask);
243+
244+
return CGF.Builder.CreateBitCast(BitVec, ResultType);
245+
}
246+
212247
/// getBuiltinLibFunction - Given a builtin id for a function like
213248
/// "__builtin_fabsf", return a Function* for "fabsf".
214249
llvm::Constant *CodeGenModule::getBuiltinLibFunction(const FunctionDecl *FD,
@@ -19023,6 +19058,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
1902319058
CGM.getHLSLRuntime().getAnyIntrinsic(), ArrayRef<Value *>{Op0}, nullptr,
1902419059
"hlsl.any");
1902519060
}
19061+
case Builtin::BI__builtin_hlsl_asdouble:
19062+
return handleAsDoubleBuiltin(*this, E);
1902619063
case Builtin::BI__builtin_hlsl_elementwise_clamp: {
1902719064
Value *OpX = EmitScalarExpr(E->getArg(0));
1902819065
Value *OpMin = EmitScalarExpr(E->getArg(1));

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,24 @@ bool any(double3);
361361
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_any)
362362
bool any(double4);
363363

364+
//===----------------------------------------------------------------------===//
365+
// asdouble builtins
366+
//===----------------------------------------------------------------------===//
367+
368+
/// \fn double asdouble(uint LowBits, uint HighBits)
369+
/// \brief Reinterprets a cast value (two 32-bit values) into a double.
370+
/// \param LowBits The low 32-bit pattern of the input value.
371+
/// \param HighBits The high 32-bit pattern of the input value.
372+
373+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_asdouble)
374+
double asdouble(uint, uint);
375+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_asdouble)
376+
double2 asdouble(uint2, uint2);
377+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_asdouble)
378+
double3 asdouble(uint3, uint3);
379+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_asdouble)
380+
double4 asdouble(uint4, uint4);
381+
364382
//===----------------------------------------------------------------------===//
365383
// asfloat builtins
366384
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,6 +1888,15 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
18881888
return true;
18891889
break;
18901890
}
1891+
case Builtin::BI__builtin_hlsl_asdouble: {
1892+
if (SemaRef.checkArgCount(TheCall, 2))
1893+
return true;
1894+
if (CheckUnsignedIntRepresentation(&SemaRef, TheCall))
1895+
return true;
1896+
1897+
SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().DoubleTy);
1898+
break;
1899+
}
18911900
case Builtin::BI__builtin_hlsl_elementwise_clamp: {
18921901
if (SemaRef.checkArgCount(TheCall, 3))
18931902
return true;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple \
2+
// RUN: dxil-pc-shadermodel6.3-compute %s -emit-llvm -disable-llvm-passes -o - | \
3+
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
4+
// RUN: %clang_cc1 -finclude-default-header -triple \
5+
// RUN: spirv-pc-vulkan-compute %s -emit-llvm -disable-llvm-passes -o - | \
6+
// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-SPV
7+
8+
// Test lowering of asdouble expansion to shuffle/bitcast and splat when required
9+
10+
// CHECK-LABEL: test_uint
11+
double test_uint(uint low, uint high) {
12+
// CHECK-SPV: %[[LOW_INSERT:.*]] = insertelement <1 x i32>
13+
// CHECK-SPV: %[[LOW_SHUFFLE:.*]] = shufflevector <1 x i32> %[[LOW_INSERT]], {{.*}} zeroinitializer
14+
// CHECK-SPV: %[[HIGH_INSERT:.*]] = insertelement <1 x i32>
15+
// CHECK-SPV: %[[HIGH_SHUFFLE:.*]] = shufflevector <1 x i32> %[[HIGH_INSERT]], {{.*}} zeroinitializer
16+
17+
// CHECK-SPV: %[[SHUFFLE0:.*]] = shufflevector <1 x i32> %[[LOW_SHUFFLE]], <1 x i32> %[[HIGH_SHUFFLE]],
18+
// CHECK-SPV-SAME: {{.*}} <i32 0, i32 1>
19+
// CHECK-SPV: bitcast <2 x i32> %[[SHUFFLE0]] to double
20+
21+
// CHECK-DXIL: call double @llvm.dx.asdouble.i32
22+
return asdouble(low, high);
23+
}
24+
25+
// CHECK-DXIL: declare double @llvm.dx.asdouble.i32
26+
27+
// CHECK-LABEL: test_vuint
28+
double3 test_vuint(uint3 low, uint3 high) {
29+
// CHECK-SPV: %[[SHUFFLE1:.*]] = shufflevector
30+
// CHECK-SPV-SAME: {{.*}} <i32 0, i32 3, i32 1, i32 4, i32 2, i32 5>
31+
// CHECK-SPV: bitcast <6 x i32> %[[SHUFFLE1]] to <3 x double>
32+
33+
// CHECK-DXIL: call <3 x double> @llvm.dx.asdouble.v3i32
34+
return asdouble(low, high);
35+
}
36+
37+
// CHECK-DXIL: declare <3 x double> @llvm.dx.asdouble.v3i32
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify
2+
3+
double test_too_few_arg() {
4+
return __builtin_hlsl_asdouble();
5+
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
6+
}
7+
8+
double test_too_few_arg_1(uint p0) {
9+
return __builtin_hlsl_asdouble(p0);
10+
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
11+
}
12+
13+
double test_too_many_arg(uint p0) {
14+
return __builtin_hlsl_asdouble(p0, p0, p0);
15+
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
16+
}

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def int_dx_cast_handle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
4646

4747
def int_dx_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem]>;
4848
def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem]>;
49+
def int_dx_asdouble : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_double_ty>], [llvm_anyint_ty, LLVMMatchType<0>], [IntrNoMem]>;
4950
def int_dx_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
5051
def int_dx_sclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;
5152
def int_dx_nclamp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>;

llvm/lib/Target/DirectX/DXIL.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,15 @@ def FlattenedThreadIdInGroup : DXILOp<96, flattenedThreadIdInGroup> {
818818
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
819819
}
820820

821+
def MakeDouble : DXILOp<101, makeDouble> {
822+
let Doc = "creates a double value";
823+
let LLVMIntrinsic = int_dx_asdouble;
824+
let arguments = [Int32Ty, Int32Ty];
825+
let result = DoubleTy;
826+
let stages = [Stages<DXIL1_0, [all_stages]>];
827+
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
828+
}
829+
821830
def SplitDouble : DXILOp<102, splitDouble> {
822831
let Doc = "Splits a double into 2 uints";
823832
let arguments = [OverloadTy];

llvm/lib/Target/DirectX/DirectXTargetTransformInfo.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ bool DirectXTTIImpl::isTargetIntrinsicWithScalarOpAtArg(Intrinsic::ID ID,
2828
bool DirectXTTIImpl::isVectorIntrinsicWithOverloadTypeAtArg(Intrinsic::ID ID,
2929
int ScalarOpdIdx) {
3030
switch (ID) {
31+
case Intrinsic::dx_asdouble:
32+
return ScalarOpdIdx == 0;
3133
default:
3234
return ScalarOpdIdx == -1;
3335
}
@@ -39,6 +41,7 @@ bool DirectXTTIImpl::isTargetIntrinsicTriviallyScalarizable(
3941
case Intrinsic::dx_frac:
4042
case Intrinsic::dx_rsqrt:
4143
case Intrinsic::dx_wave_readlane:
44+
case Intrinsic::dx_asdouble:
4245
case Intrinsic::dx_splitdouble:
4346
case Intrinsic::dx_firstbituhigh:
4447
case Intrinsic::dx_firstbitshigh:

llvm/test/CodeGen/DirectX/asdouble.ll

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
; RUN: opt -S -scalarizer -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
2+
3+
; Test that for scalar and vector inputs, asdouble maps down to the makeDouble
4+
; DirectX op
5+
6+
define noundef double @asdouble_scalar(i32 noundef %low, i32 noundef %high) {
7+
; CHECK: call double @dx.op.makeDouble(i32 101, i32 %low, i32 %high)
8+
%ret = call double @llvm.dx.asdouble.i32(i32 %low, i32 %high)
9+
ret double %ret
10+
}
11+
12+
declare double @llvm.dx.asdouble.i32(i32, i32)
13+
14+
define noundef <3 x double> @asdouble_vec(<3 x i32> noundef %low, <3 x i32> noundef %high) {
15+
; CHECK: call double @dx.op.makeDouble(i32 101, i32 %low.i0, i32 %high.i0)
16+
; CHECK: call double @dx.op.makeDouble(i32 101, i32 %low.i1, i32 %high.i1)
17+
; CHECK: call double @dx.op.makeDouble(i32 101, i32 %low.i2, i32 %high.i2)
18+
%ret = call <3 x double> @llvm.dx.asdouble.v3i32(<3 x i32> %low, <3 x i32> %high)
19+
ret <3 x double> %ret
20+
}
21+
22+
declare <3 x double> @llvm.dx.asdouble.v3i32(<3 x i32>, <3 x i32>)

0 commit comments

Comments
 (0)