-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Add normalize builtins and normalize HLSL function to DirectX and SPIR-V backend #102683
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
547b4da
e3ca0f0
bd40352
45a7ff7
6480d2d
76a2d07
a9188ad
13102f6
3610e91
d0bea33
dfef7cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ | ||
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \ | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ | ||
// RUN: --check-prefixes=CHECK,DXIL_CHECK,DXIL_NATIVE_HALF,NATIVE_HALF | ||
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ | ||
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \ | ||
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,DXIL_CHECK,NO_HALF,DXIL_NO_HALF | ||
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ | ||
// RUN: spirv-unknown-vulkan-compute %s -fnative-half-type \ | ||
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ | ||
// RUN: --check-prefixes=CHECK,NATIVE_HALF,SPIR_NATIVE_HALF,SPIR_CHECK | ||
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ | ||
// RUN: spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \ | ||
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF,SPIR_NO_HALF,SPIR_CHECK | ||
|
||
// DXIL_NATIVE_HALF: define noundef half @ | ||
// SPIR_NATIVE_HALF: define spir_func noundef half @ | ||
// DXIL_NATIVE_HALF: call half @llvm.dx.normalize.f16(half | ||
// SPIR_NATIVE_HALF: call half @llvm.spv.normalize.f16(half | ||
// DXIL_NO_HALF: call float @llvm.dx.normalize.f32(float | ||
// SPIR_NO_HALF: call float @llvm.spv.normalize.f32(float | ||
// NATIVE_HALF: ret half | ||
// NO_HALF: ret float | ||
half test_normalize_half(half p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_NATIVE_HALF: define noundef <2 x half> @ | ||
// SPIR_NATIVE_HALF: define spir_func noundef <2 x half> @ | ||
// DXIL_NATIVE_HALF: call <2 x half> @llvm.dx.normalize.v2f16(<2 x half> | ||
// SPIR_NATIVE_HALF: call <2 x half> @llvm.spv.normalize.v2f16(<2 x half> | ||
// DXIL_NO_HALF: call <2 x float> @llvm.dx.normalize.v2f32(<2 x float> | ||
// SPIR_NO_HALF: call <2 x float> @llvm.spv.normalize.v2f32(<2 x float> | ||
// NATIVE_HALF: ret <2 x half> %hlsl.normalize | ||
// NO_HALF: ret <2 x float> %hlsl.normalize | ||
half2 test_normalize_half2(half2 p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_NATIVE_HALF: define noundef <3 x half> @ | ||
// SPIR_NATIVE_HALF: define spir_func noundef <3 x half> @ | ||
// DXIL_NATIVE_HALF: call <3 x half> @llvm.dx.normalize.v3f16(<3 x half> | ||
// SPIR_NATIVE_HALF: call <3 x half> @llvm.spv.normalize.v3f16(<3 x half> | ||
// DXIL_NO_HALF: call <3 x float> @llvm.dx.normalize.v3f32(<3 x float> | ||
// SPIR_NO_HALF: call <3 x float> @llvm.spv.normalize.v3f32(<3 x float> | ||
// NATIVE_HALF: ret <3 x half> %hlsl.normalize | ||
// NO_HALF: ret <3 x float> %hlsl.normalize | ||
half3 test_normalize_half3(half3 p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_NATIVE_HALF: define noundef <4 x half> @ | ||
// SPIR_NATIVE_HALF: define spir_func noundef <4 x half> @ | ||
// DXIL_NATIVE_HALF: call <4 x half> @llvm.dx.normalize.v4f16(<4 x half> | ||
// SPIR_NATIVE_HALF: call <4 x half> @llvm.spv.normalize.v4f16(<4 x half> | ||
// DXIL_NO_HALF: call <4 x float> @llvm.dx.normalize.v4f32(<4 x float> | ||
// SPIR_NO_HALF: call <4 x float> @llvm.spv.normalize.v4f32(<4 x float> | ||
// NATIVE_HALF: ret <4 x half> %hlsl.normalize | ||
// NO_HALF: ret <4 x float> %hlsl.normalize | ||
half4 test_normalize_half4(half4 p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
|
||
// DXIL_CHECK: define noundef float @ | ||
// SPIR_CHECK: define spir_func noundef float @ | ||
// DXIL_CHECK: call float @llvm.dx.normalize.f32(float | ||
// SPIR_CHECK: call float @llvm.spv.normalize.f32(float | ||
// CHECK: ret float | ||
float test_normalize_float(float p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_CHECK: define noundef <2 x float> @ | ||
// SPIR_CHECK: define spir_func noundef <2 x float> @ | ||
// DXIL_CHECK: %hlsl.normalize = call <2 x float> @llvm.dx.normalize.v2f32( | ||
// SPIR_CHECK: %hlsl.normalize = call <2 x float> @llvm.spv.normalize.v2f32(<2 x float> | ||
// CHECK: ret <2 x float> %hlsl.normalize | ||
float2 test_normalize_float2(float2 p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_CHECK: define noundef <3 x float> @ | ||
// SPIR_CHECK: define spir_func noundef <3 x float> @ | ||
// DXIL_CHECK: %hlsl.normalize = call <3 x float> @llvm.dx.normalize.v3f32( | ||
// SPIR_CHECK: %hlsl.normalize = call <3 x float> @llvm.spv.normalize.v3f32(<3 x float> | ||
// CHECK: ret <3 x float> %hlsl.normalize | ||
float3 test_normalize_float3(float3 p0) | ||
{ | ||
return normalize(p0); | ||
} | ||
// DXIL_CHECK: define noundef <4 x float> @ | ||
// SPIR_CHECK: define spir_func noundef <4 x float> @ | ||
// DXIL_CHECK: %hlsl.normalize = call <4 x float> @llvm.dx.normalize.v4f32( | ||
// SPIR_CHECK: %hlsl.normalize = call <4 x float> @llvm.spv.normalize.v4f32( | ||
// CHECK: ret <4 x float> %hlsl.normalize | ||
float4 test_length_float4(float4 p0) | ||
{ | ||
return normalize(p0); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm -disable-llvm-passes -verify -verify-ignore-unexpected | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
void test_too_few_arg() | ||
{ | ||
return __builtin_hlsl_normalize(); | ||
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}} | ||
} | ||
|
||
void test_too_many_arg(float2 p0) | ||
{ | ||
return __builtin_hlsl_normalize(p0, p0); | ||
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}} | ||
} | ||
|
||
bool builtin_bool_to_float_type_promotion(bool p1) | ||
{ | ||
return __builtin_hlsl_normalize(p1); | ||
// expected-error@-1 {passing 'bool' to parameter of incompatible type 'float'}} | ||
} | ||
|
||
bool builtin_normalize_int_to_float_promotion(int p1) | ||
{ | ||
return __builtin_hlsl_normalize(p1); | ||
// expected-error@-1 {{passing 'int' to parameter of incompatible type 'float'}} | ||
} | ||
|
||
bool2 builtin_normalize_int2_to_float2_promotion(int2 p1) | ||
{ | ||
return __builtin_hlsl_normalize(p1); | ||
// expected-error@-1 {{passing 'int2' (aka 'vector<int, 2>') to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(float)))) float' (vector of 2 'float' values)}} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ static bool isIntrinsicExpansion(Function &F) { | |
case Intrinsic::dx_uclamp: | ||
case Intrinsic::dx_lerp: | ||
case Intrinsic::dx_length: | ||
case Intrinsic::dx_normalize: | ||
case Intrinsic::dx_sdot: | ||
case Intrinsic::dx_udot: | ||
return true; | ||
|
@@ -229,6 +230,75 @@ static bool expandLog10Intrinsic(CallInst *Orig) { | |
return expandLogIntrinsic(Orig, numbers::ln2f / numbers::ln10f); | ||
} | ||
|
||
static bool expandNormalizeIntrinsic(CallInst *Orig) { | ||
Value *X = Orig->getOperand(0); | ||
Type *Ty = Orig->getType(); | ||
Type *EltTy = Ty->getScalarType(); | ||
IRBuilder<> Builder(Orig->getParent()); | ||
Builder.SetInsertPoint(Orig); | ||
|
||
auto *XVec = dyn_cast<FixedVectorType>(Ty); | ||
if (!XVec) { | ||
if (auto *constantFP = dyn_cast<ConstantFP>(X)) { | ||
const APFloat &fpVal = constantFP->getValueAPF(); | ||
if (fpVal.isZero()) | ||
report_fatal_error(Twine("Invalid input scalar: length is zero"), | ||
/* gen_crash_diag=*/false); | ||
} | ||
Value *Result = Builder.CreateFDiv(X, X); | ||
|
||
Orig->replaceAllUsesWith(Result); | ||
Orig->eraseFromParent(); | ||
return true; | ||
} | ||
|
||
Value *Elt = Builder.CreateExtractElement(X, (uint64_t)0); | ||
bob80905 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
unsigned XVecSize = XVec->getNumElements(); | ||
Value *DotProduct = nullptr; | ||
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. For lines 256 to 279 is something that will have to be cleaned up into a helper function. I think we are going to have some code duplication here @pow2clk is moving 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. Understood, I'll make the change if Greg's comes in first. |
||
// use the dot intrinsic corresponding to the vector size | ||
switch (XVecSize) { | ||
case 1: | ||
report_fatal_error(Twine("Invalid input vector: length is zero"), | ||
/* gen_crash_diag=*/false); | ||
break; | ||
case 2: | ||
DotProduct = Builder.CreateIntrinsic( | ||
EltTy, Intrinsic::dx_dot2, ArrayRef<Value *>{X, X}, nullptr, "dx.dot2"); | ||
break; | ||
case 3: | ||
DotProduct = Builder.CreateIntrinsic( | ||
EltTy, Intrinsic::dx_dot3, ArrayRef<Value *>{X, X}, nullptr, "dx.dot3"); | ||
break; | ||
case 4: | ||
DotProduct = Builder.CreateIntrinsic( | ||
EltTy, Intrinsic::dx_dot4, ArrayRef<Value *>{X, X}, nullptr, "dx.dot4"); | ||
break; | ||
default: | ||
report_fatal_error(Twine("Invalid input vector: vector size is invalid."), | ||
/* gen_crash_diag=*/false); | ||
} | ||
|
||
// verify that the length is non-zero | ||
// (if the dot product is non-zero, then the length is non-zero) | ||
if (auto *constantFP = dyn_cast<ConstantFP>(DotProduct)) { | ||
const APFloat &fpVal = constantFP->getValueAPF(); | ||
if (fpVal.isZero()) | ||
report_fatal_error(Twine("Invalid input vector: length is zero"), | ||
/* gen_crash_diag=*/false); | ||
} | ||
|
||
Value *Multiplicand = Builder.CreateIntrinsic(EltTy, Intrinsic::dx_rsqrt, | ||
ArrayRef<Value *>{DotProduct}, | ||
nullptr, "dx.rsqrt"); | ||
|
||
Value *MultiplicandVec = Builder.CreateVectorSplat(XVecSize, Multiplicand); | ||
Value *Result = Builder.CreateFMul(X, MultiplicandVec); | ||
|
||
Orig->replaceAllUsesWith(Result); | ||
Orig->eraseFromParent(); | ||
return true; | ||
} | ||
|
||
static bool expandPowIntrinsic(CallInst *Orig) { | ||
|
||
Value *X = Orig->getOperand(0); | ||
|
@@ -314,6 +384,8 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) { | |
return expandLerpIntrinsic(Orig); | ||
case Intrinsic::dx_length: | ||
return expandLengthIntrinsic(Orig); | ||
case Intrinsic::dx_normalize: | ||
return expandNormalizeIntrinsic(Orig); | ||
case Intrinsic::dx_sdot: | ||
case Intrinsic::dx_udot: | ||
return expandIntegerDot(Orig, F.getIntrinsicID()); | ||
|
Uh oh!
There was an error while loading. Please reload this page.