Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -5235,6 +5235,12 @@ def HLSLGetSpirvSpecConstant : LangBuiltin<"HLSL_LANG">, HLSLScalarTemplate {
let Prototype = "T(unsigned int, T)";
}

def HLSLF16ToF32 : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_f16tof32"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
54 changes: 54 additions & 0 deletions clang/lib/CodeGen/CGHLSLBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,57 @@ static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) {
return LastInst;
}

static Value *handleElementwiseF16ToF32(CodeGenFunction &CGF,
const CallExpr *E) {
Value *Op0 = CGF.EmitScalarExpr(E->getArg(0));
QualType Op0Ty = E->getArg(0)->getType();
llvm::Type *ResType = CGF.FloatTy;
uint64_t NumElements = 0;
if (Op0->getType()->isVectorTy()) {
NumElements =
E->getArg(0)->getType()->castAs<clang::VectorType>()->getNumElements();
ResType =
llvm::VectorType::get(ResType, ElementCount::getFixed(NumElements));
}
if (!Op0Ty->hasUnsignedIntegerRepresentation())
llvm_unreachable(
"f16tof32 operand must have an unsigned int representation");

if (CGF.CGM.getTriple().isDXIL())
return CGF.Builder.CreateIntrinsic(ResType, Intrinsic::dx_legacyf16tof32,
ArrayRef<Value *>{Op0}, nullptr,
"hlsl.f16tof32");

if (CGF.CGM.getTriple().isSPIRV()) {
// We use the SPIRV UnpackHalf2x16 operation to avoid the need for the
// Int16 and Float16 capabilities
auto UnpackType =
llvm::VectorType::get(CGF.FloatTy, ElementCount::getFixed(2));
if (NumElements == 0) {
// a scalar input - simply extract the first element of the unpacked
// vector
Value *Unpack = CGF.Builder.CreateIntrinsic(
UnpackType, Intrinsic::spv_unpackhalf2x16, ArrayRef<Value *>{Op0});
return CGF.Builder.CreateExtractElement(Unpack, (uint64_t)0);
} else {
// a vector input - build a congruent output vector by iterating through
// the input vector calling unpackhalf2x16 for each element
Value *Result = PoisonValue::get(ResType);
for (uint64_t i = 0; i < NumElements; i++) {
Value *InVal = CGF.Builder.CreateExtractElement(Op0, i);
Value *Unpack = CGF.Builder.CreateIntrinsic(
UnpackType, Intrinsic::spv_unpackhalf2x16,
ArrayRef<Value *>{InVal});
Value *Res = CGF.Builder.CreateExtractElement(Unpack, (uint64_t)0);
Result = CGF.Builder.CreateInsertElement(Result, Res, i);
}
return Result;
}
}

llvm_unreachable("Intrinsic F16ToF32 not supported by target architecture");
}

static Value *emitBufferStride(CodeGenFunction *CGF, const Expr *HandleExpr,
LValue &Stride) {
// Figure out the stride of the buffer elements from the handle type.
Expand Down Expand Up @@ -579,6 +630,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
/*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getDegreesIntrinsic(),
ArrayRef<Value *>{X}, nullptr, "hlsl.degrees");
}
case Builtin::BI__builtin_hlsl_elementwise_f16tof32: {
return handleElementwiseF16ToF32(*this, E);
}
case Builtin::BI__builtin_hlsl_elementwise_frac: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,27 @@ float3 exp2(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_exp2)
float4 exp2(float4);

//===----------------------------------------------------------------------===//
// f16tof32 builtins
//===----------------------------------------------------------------------===//

/// \fn float f16tof32(uint x)
/// \brief Returns the half value stored in the low 16 bits of the uint arg
/// converted to a float.
/// \param x The uint containing two half values.
///
/// The float value of the half value found in the low 16 bits of the \a xi
/// parameter.

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_f16tof32)
float f16tof32(uint);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_f16tof32)
float2 f16tof32(uint2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_f16tof32)
float3 f16tof32(uint3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_f16tof32)
float4 f16tof32(uint4);

//===----------------------------------------------------------------------===//
// firstbithigh builtins
//===----------------------------------------------------------------------===//
Expand Down
57 changes: 45 additions & 12 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2802,6 +2802,23 @@ static bool CheckUnsignedIntRepresentation(Sema *S, SourceLocation Loc,
return false;
}

static bool CheckExpectedBitWidth(Sema *S, CallExpr *TheCall,
unsigned ArgOrdinal, unsigned Width) {
QualType ArgTy = TheCall->getArg(0)->getType();
if (auto *VTy = ArgTy->getAs<VectorType>())
ArgTy = VTy->getElementType();
// ensure arg type has expected bit width
uint64_t ElementBitCount =
S->getASTContext().getTypeSizeInChars(ArgTy).getQuantity() * 8;
if (ElementBitCount != Width) {
S->Diag(TheCall->getArg(0)->getBeginLoc(),
diag::err_integer_incorrect_bit_count)
<< Width << ElementBitCount;
return true;
}
return false;
}

static void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall,
QualType ReturnType) {
auto *VecTyA = TheCall->getArg(0)->getType()->getAs<VectorType>();
Expand Down Expand Up @@ -2961,24 +2978,16 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
CheckUnsignedIntVecRepresentation))
return true;

auto *VTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
// ensure arg integers are 32-bits
uint64_t ElementBitCount = getASTContext()
.getTypeSizeInChars(VTy->getElementType())
.getQuantity() *
8;
if (ElementBitCount != 32) {
SemaRef.Diag(TheCall->getBeginLoc(),
diag::err_integer_incorrect_bit_count)
<< 32 << ElementBitCount;
if (CheckExpectedBitWidth(&SemaRef, TheCall, 0, 32))
return true;
}

// ensure both args are vectors of total bit size of a multiple of 64
auto *VTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
int NumElementsArg = VTy->getNumElements();
if (NumElementsArg != 2 && NumElementsArg != 4) {
SemaRef.Diag(TheCall->getBeginLoc(), diag::err_vector_incorrect_bit_count)
<< 1 /*a multiple of*/ << 64 << NumElementsArg * ElementBitCount;
<< 1 /*a multiple of*/ << 64 << NumElementsArg * 32;
return true;
}

Expand Down Expand Up @@ -3295,7 +3304,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
break;
}
// Note these are llvm builtins that we want to catch invalid intrinsic
// generation. Normal handling of these builitns will occur elsewhere.
// generation. Normal handling of these builtins will occur elsewhere.
case Builtin::BI__builtin_elementwise_bitreverse: {
// does not include a check for number of arguments
// because that is done previously
Expand Down Expand Up @@ -3405,6 +3414,30 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
}
break;
}
case Builtin::BI__builtin_hlsl_elementwise_f16tof32: {
if (SemaRef.checkArgCount(TheCall, 1))
return true;
if (CheckAllArgTypesAreCorrect(&SemaRef, TheCall,
CheckUnsignedIntRepresentation))
return true;
// ensure arg integers are 32 bits
if (CheckExpectedBitWidth(&SemaRef, TheCall, 0, 32))
return true;
// check it wasn't a bool type
QualType ArgTy = TheCall->getArg(0)->getType();
if (auto *VTy = ArgTy->getAs<VectorType>())
ArgTy = VTy->getElementType();
if (ArgTy->isBooleanType()) {
SemaRef.Diag(TheCall->getArg(0)->getBeginLoc(),
diag::err_builtin_invalid_arg_type)
<< 1 << /* scalar or vector of */ 5 << /* unsigned int */ 3
<< /* no fp */ 0 << TheCall->getArg(0)->getType();
return true;
}

SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().FloatTy);
break;
}
}
return false;
}
Expand Down
30 changes: 30 additions & 0 deletions clang/test/CodeGenHLSL/builtins/f16tof32-builtin.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s

// CHECK: define hidden noundef nofpclass(nan inf) float
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn float @llvm.dx.legacyf16tof32.i32(i32 %0)
// CHECK: ret float %hlsl.f16tof32
// CHECK: declare float @llvm.dx.legacyf16tof32.i32(i32)
float test_scalar(uint p0) { return __builtin_hlsl_elementwise_f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <2 x float>
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.dx.legacyf16tof32.v2i32(<2 x i32> %0)
// CHECK: ret <2 x float> %hlsl.f16tof32
// CHECK: declare <2 x float> @llvm.dx.legacyf16tof32.v2i32(<2 x i32>)
float2 test_uint2(uint2 p0) { return __builtin_hlsl_elementwise_f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <3 x float> @_Z10test_uint3Dv3_j(<3 x i32> noundef %p0) #0 {
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.dx.legacyf16tof32.v3i32(<3 x i32> %0)
// CHECK: ret <3 x float> %hlsl.f16tof32
// CHECK: declare <3 x float> @llvm.dx.legacyf16tof32.v3i32(<3 x i32>)
float3 test_uint3(uint3 p0) { return __builtin_hlsl_elementwise_f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <4 x float> @_Z10test_uint4Dv4_j(<4 x i32> noundef %p0) #0 {
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.dx.legacyf16tof32.v4i32(<4 x i32> %0)
// CHECK: ret <4 x float> %hlsl.f16tof32
// CHECK: declare <4 x float> @llvm.dx.legacyf16tof32.v4i32(<4 x i32>)
float4 test_uint4(uint4 p0) { return __builtin_hlsl_elementwise_f16tof32(p0); }



30 changes: 30 additions & 0 deletions clang/test/CodeGenHLSL/builtins/f16tof32.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s

// CHECK: define hidden noundef nofpclass(nan inf) float
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn float @llvm.dx.legacyf16tof32.i32(i32 %0)
// CHECK: ret float %hlsl.f16tof32
// CHECK: declare float @llvm.dx.legacyf16tof32.i32(i32)
float test_scalar(uint p0) { return f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <2 x float>
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.dx.legacyf16tof32.v2i32(<2 x i32> %0)
// CHECK: ret <2 x float> %hlsl.f16tof32
// CHECK: declare <2 x float> @llvm.dx.legacyf16tof32.v2i32(<2 x i32>)
float2 test_uint2(uint2 p0) { return f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <3 x float> @_Z10test_uint3Dv3_j(<3 x i32> noundef %p0) #0 {
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.dx.legacyf16tof32.v3i32(<3 x i32> %0)
// CHECK: ret <3 x float> %hlsl.f16tof32
// CHECK: declare <3 x float> @llvm.dx.legacyf16tof32.v3i32(<3 x i32>)
float3 test_uint3(uint3 p0) { return f16tof32(p0); }

// CHECK: define hidden noundef nofpclass(nan inf) <4 x float> @_Z10test_uint4Dv4_j(<4 x i32> noundef %p0) #0 {
// CHECK: %hlsl.f16tof32 = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.dx.legacyf16tof32.v4i32(<4 x i32> %0)
// CHECK: ret <4 x float> %hlsl.f16tof32
// CHECK: declare <4 x float> @llvm.dx.legacyf16tof32.v4i32(<4 x i32>)
float4 test_uint4(uint4 p0) { return f16tof32(p0); }



134 changes: 134 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/f16tof32-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.6-library %s -fnative-int16-type -emit-llvm-only -disable-llvm-passes -verify

float builtin_f16tof32_too_few_arg() {
return __builtin_hlsl_elementwise_f16tof32();
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
// expected-note@hlsl/hlsl_alias_intrinsics.h:* 4 {{candidate function not viable: requires 1 argument, but 0 were provided}}
}

float builtin_f16tof32_too_many_arg(uint p0) {
return __builtin_hlsl_elementwise_f16tof32(p0, p0);
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
// expected-note@hlsl/hlsl_alias_intrinsics.h:* 4 {{candidate function not viable: requires 1 argument, but 2 were provided}}
}

float builtin_f16tof32_bool(bool p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool')}}
}

float builtin_f16tof32_bool4(bool4 p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool4' (aka 'vector<bool, 4>')}}
}

float builtin_f16tof32_short(short p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'short')}}
}

float builtin_f16tof32_unsigned_short(unsigned short p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{incorrect number of bits in integer (expected 32 bits, have 16)}}
}

float builtin_f16tof32_int(int p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int')}}
}

float builtin_f16tof32_int64_t(long p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'long')}}
}

float2 builtin_f16tof32_int2_to_float2_promotion(int2 p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int2' (aka 'vector<int, 2>'))}}
}

float builtin_f16tof32_half(half p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half')}}
}

float builtin_f16tof32_half4(half4 p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half4' (aka 'vector<half, 4>'))}}
}

float builtin_f16tof32_float(float p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'float')}}
}

float builtin_f16tof32_double(double p0) {
return __builtin_hlsl_elementwise_f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'double')}}
}

float f16tof32_too_few_arg() {
return f16tof32();
// expected-error@-1 {{no matching function for call to 'f16tof32'}}
}

float f16tof32_too_many_arg(uint p0) {
return f16tof32(p0, p0);
// expected-error@-1 {{no matching function for call to 'f16tof32'}}
}

float f16tof32_bool(bool p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool')}}
}

float f16tof32_bool3(bool3 p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'bool3' (aka 'vector<bool, 3>'))}}
}


float f16tof32_int16_t(short p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'short')}}
}

float f16tof32_int16_t(unsigned short p0) {
return f16tof32(p0);
// expected-error@-1 {{incorrect number of bits in integer (expected 32 bits, have 16)}}
}

float f16tof32_int(int p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int')}}
}

float f16tof32_int64_t(long p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'long')}}
}

float2 f16tof32_int2_to_float2_promotion(int3 p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'int3' (aka 'vector<int, 3>'))}}
}

float f16tof32_half(half p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half')}}
}

float f16tof32_half2(half2 p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'half2' (aka 'vector<half, 2>'))}}
}

float f16tof32_float(float p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'float')}}
}

float f16tof32_double(double p0) {
return f16tof32(p0);
// expected-error@-1 {{1st argument must be a scalar or vector of unsigned integer types (was 'double')}}
}
Loading