From ce69ac763f3dd54c829e657fa35a591e58c4833a Mon Sep 17 00:00:00 2001 From: "Sidorov, Dmitry" Date: Thu, 10 Oct 2024 10:47:47 -0700 Subject: [PATCH 1/3] Add fast math flag translation for OpenCL std lib Such possibility was added in SPIR-V 1.6. This patch also introduces limited translation of nofpclass LLVM parameter attribute. Signed-off-by: Sidorov, Dmitry --- lib/SPIRV/SPIRVBuiltinHelper.cpp | 3 + lib/SPIRV/SPIRVReader.cpp | 7 +- lib/SPIRV/SPIRVWriter.cpp | 45 ++++++++++- test/transcoding/fast-math-opencl-builtins.ll | 78 +++++++++++++++++++ 4 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 test/transcoding/fast-math-opencl-builtins.ll diff --git a/lib/SPIRV/SPIRVBuiltinHelper.cpp b/lib/SPIRV/SPIRVBuiltinHelper.cpp index f092d110c1..2c6986ad0f 100644 --- a/lib/SPIRV/SPIRVBuiltinHelper.cpp +++ b/lib/SPIRV/SPIRVBuiltinHelper.cpp @@ -106,6 +106,9 @@ Value *BuiltinCallMutator::doConversion() { NewCall->copyMetadata(*CI); NewCall->setAttributes(CallAttrs); NewCall->setTailCall(CI->isTailCall()); + if (isa(CI)) + NewCall->setFastMathFlags(CI->getFastMathFlags()); + if (CI->hasFnAttr("fpbuiltin-max-error")) { auto Attr = CI->getFnAttr("fpbuiltin-max-error"); NewCall->addFnAttr(Attr); diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index bd4d877c3a..4b3dae1e86 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -2474,8 +2474,11 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, case OpExtInst: { auto *ExtInst = static_cast(BV); switch (ExtInst->getExtSetKind()) { - case SPIRVEIS_OpenCL: - return mapValue(BV, transOCLBuiltinFromExtInst(ExtInst, BB)); + case SPIRVEIS_OpenCL: { + auto *V = mapValue(BV, transOCLBuiltinFromExtInst(ExtInst, BB)); + applyFPFastMathModeDecorations(BV, static_cast(V)); + return V; + } case SPIRVEIS_Debug: case SPIRVEIS_OpenCL_DebugInfo_100: case SPIRVEIS_NonSemantic_Shader_DebugInfo_100: diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index 6b94cb0210..be8fbc4c9b 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -3038,7 +3038,8 @@ bool LLVMToSPIRVBase::transDecoration(Value *V, SPIRVValue *BV) { if (Opcode == Instruction::FAdd || Opcode == Instruction::FSub || Opcode == Instruction::FMul || Opcode == Instruction::FDiv || Opcode == Instruction::FRem || - ((Opcode == Instruction::FNeg || Opcode == Instruction::FCmp) && + ((Opcode == Instruction::FNeg || Opcode == Instruction::FCmp || + BV->isExtInst()) && BM->isAllowedToUseVersion(VersionNumber::SPIRV_1_6))) { FastMathFlags FMF = BVF->getFastMathFlags(); SPIRVWord M{0}; @@ -3065,8 +3066,48 @@ bool LLVMToSPIRVBase::transDecoration(Value *V, SPIRVValue *BV) { } } } - if (M != 0) + // Handle nofpclass attribute. Nothing to do if fast math flag is already + // set. + if ((BV->isExtInst() && static_cast( + BV)->getExtSetKind() == SPIRVEIS_OpenCL) && + BM->isAllowedToUseVersion(VersionNumber::SPIRV_1_6) && + !(M & FPFastMathModeFastMask)) { + auto *F = cast(V)->getCalledFunction(); + auto FAttrs = F->getAttributes(); + AttributeSet RetAttrs = FAttrs.getRetAttrs(); + if (RetAttrs.hasAttribute(Attribute::NoFPClass)) { + FPClassTest RetTest = RetAttrs.getAttribute( + Attribute::NoFPClass).getNoFPClass(); + AttributeSet RetAttrs = FAttrs.getRetAttrs(); + // Only Nan and Inf tests are representable in SPIR-V now. + bool ToAddNoNan = RetTest & fcNan; + bool ToAddNoInf = RetTest & fcInf; + if (ToAddNoNan || ToAddNoInf) { + const size_t NumParams = F->getFunctionType()->getNumParams(); + for (size_t I = 0; I != NumParams; ++I) { + if (!F->hasParamAttribute(I, Attribute::NoFPClass)) { + ToAddNoNan = false; + ToAddNoInf = false; + break; + } + FPClassTest ArgTest = + FAttrs.getParamAttr(I, Attribute::NoFPClass).getNoFPClass(); + ToAddNoNan = ToAddNoNan && static_cast(ArgTest & fcNan); + ToAddNoInf = ToAddNoInf && static_cast(ArgTest & fcInf); + } + } + if (ToAddNoNan) + M |= FPFastMathModeNotNaNMask; + if (ToAddNoInf) + M |= FPFastMathModeNotInfMask; + } + } + if (M != 0) { BV->setFPFastMathMode(M); + if (Opcode == Instruction::FNeg || Opcode == Instruction::FCmp || + BV->isExtInst()) + BM->setMinSPIRVVersion(VersionNumber::SPIRV_1_6); + } } } if (Instruction *Inst = dyn_cast(V)) { diff --git a/test/transcoding/fast-math-opencl-builtins.ll b/test/transcoding/fast-math-opencl-builtins.ll new file mode 100644 index 0000000000..ce0e672b7a --- /dev/null +++ b/test/transcoding/fast-math-opencl-builtins.ll @@ -0,0 +1,78 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-spirv -spirv-text %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV +; RUN: llvm-spirv %t.bc -o %t.spv +; RUN: spirv-val %t.spv +; RUN: llvm-spirv -r %t.spv -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM-OCL +; RUN: llvm-spirv -r --spirv-target-env=SPV-IR %t.spv -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM-SPV + +; RUN: llvm-spirv -spirv-text --spirv-max-version=1.5 %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV-NEG + +; CHECK-SPIRV: Decorate [[#FPDec1:]] FPFastMathMode 3 +; CHECK-SPIRV: Decorate [[#FPDec2:]] FPFastMathMode 2 +; CHECK-SPIRV: Decorate [[#FPDec3:]] FPFastMathMode 16 +; CHECK-SPIRV: ExtInst [[#]] [[#FPDec1]] [[#]] fmax [[#]] [[#]] +; CHECK-SPIRV: ExtInst [[#]] [[#FPDec2]] [[#]] fmin [[#]] [[#]] +; CHECK-SPIRV: ExtInst [[#]] [[#FPDec3]] [[#]] fmax [[#]] [[#]] + +; CHECK-SPIRV-NEG-NOT: Decorate [[#]] FPFastMathMode [[#]] + +; CHECK-LLVM-OCL: call nnan ninf spir_func float @_Z4fmaxff(float %[[#]], float %[[#]]) +; CHECK-LLVM-OCL: call ninf spir_func float @_Z4fminff(float %[[#]], float %[[#]]) +; CHECK-LLVM-OCL: call fast spir_func float @_Z4fmaxff(float %[[#]], float %[[#]]) + +; CHECK-LLVM-SPV: call nnan ninf spir_func float @_Z16__spirv_ocl_fmaxff(float %[[#]], float %[[#]]) +; CHECK-LLVM-SPV: call ninf spir_func float @_Z16__spirv_ocl_fminff(float %[[#]], float %[[#]]) +; CHECK-LLVM-SPV: call fast spir_func float @_Z16__spirv_ocl_fmaxff(float %[[#]], float %[[#]]) + +; ModuleID = 'test.bc' +source_filename = "test.cpp" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +@__spirv_BuiltInGlobalInvocationId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 + +declare dso_local spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fmaxff(float noundef nofpclass(nan inf), float noundef nofpclass(nan inf)) local_unnamed_addr + +declare dso_local spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fminff(float noundef nofpclass(inf), float noundef nofpclass(nan inf)) local_unnamed_addr + +define weak_odr dso_local spir_kernel void @nofpclass_all(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { +entry: + %0 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId, align 32 + %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %_arg_data, i64 %0 + %arrayidx3.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat1, i64 %0 + %cmp.i = icmp ult i64 %0, 2147483648 + %arrayidx5.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat2, i64 %0 + %1 = load float, ptr addrspace(1) %arrayidx3.i, align 4 + %2 = load float, ptr addrspace(1) %arrayidx5.i, align 4 + %call.i.i = tail call spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fmaxff(float noundef nofpclass(nan inf) %1, float noundef nofpclass(nan inf) %2) + store float %call.i.i, ptr addrspace(1) %arrayidx.i, align 4 + ret void +} + +define weak_odr dso_local spir_kernel void @nofpclass_part(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { +entry: + %0 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId, align 32 + %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %_arg_data, i64 %0 + %arrayidx3.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat1, i64 %0 + %cmp.i = icmp ult i64 %0, 2147483648 + %arrayidx5.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat2, i64 %0 + %1 = load float, ptr addrspace(1) %arrayidx3.i, align 4 + %2 = load float, ptr addrspace(1) %arrayidx5.i, align 4 + %call.i.i = tail call spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fminff(float noundef nofpclass(inf) %1, float noundef nofpclass(nan inf) %2) + store float %call.i.i, ptr addrspace(1) %arrayidx.i, align 4 + ret void +} + +define weak_odr dso_local spir_kernel void @nofpclass_fast(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { +entry: + %0 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId, align 32 + %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %_arg_data, i64 %0 + %arrayidx3.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat1, i64 %0 + %cmp.i = icmp ult i64 %0, 2147483648 + %arrayidx5.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat2, i64 %0 + %1 = load float, ptr addrspace(1) %arrayidx3.i, align 4 + %2 = load float, ptr addrspace(1) %arrayidx5.i, align 4 + %call.i.i = tail call fast spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fmaxff(float noundef nofpclass(nan inf) %1, float noundef nofpclass(nan inf) %2) + store float %call.i.i, ptr addrspace(1) %arrayidx.i, align 4 + ret void +} From 0de7ccb5d6f5ac30edfbb043b4b912551afc6837 Mon Sep 17 00:00:00 2001 From: "Sidorov, Dmitry" Date: Wed, 16 Oct 2024 05:46:46 -0700 Subject: [PATCH 2/3] format Signed-off-by: Sidorov, Dmitry --- lib/SPIRV/SPIRVReader.cpp | 2 +- lib/SPIRV/SPIRVWriter.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 4b3dae1e86..a82f6b9492 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -2476,7 +2476,7 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, switch (ExtInst->getExtSetKind()) { case SPIRVEIS_OpenCL: { auto *V = mapValue(BV, transOCLBuiltinFromExtInst(ExtInst, BB)); - applyFPFastMathModeDecorations(BV, static_cast(V)); + applyFPFastMathModeDecorations(BV, static_cast(V)); return V; } case SPIRVEIS_Debug: diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index be8fbc4c9b..cea4e2d1b1 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -3068,16 +3068,17 @@ bool LLVMToSPIRVBase::transDecoration(Value *V, SPIRVValue *BV) { } // Handle nofpclass attribute. Nothing to do if fast math flag is already // set. - if ((BV->isExtInst() && static_cast( - BV)->getExtSetKind() == SPIRVEIS_OpenCL) && + if ((BV->isExtInst() && + static_cast(BV)->getExtSetKind() == + SPIRVEIS_OpenCL) && BM->isAllowedToUseVersion(VersionNumber::SPIRV_1_6) && !(M & FPFastMathModeFastMask)) { auto *F = cast(V)->getCalledFunction(); auto FAttrs = F->getAttributes(); AttributeSet RetAttrs = FAttrs.getRetAttrs(); if (RetAttrs.hasAttribute(Attribute::NoFPClass)) { - FPClassTest RetTest = RetAttrs.getAttribute( - Attribute::NoFPClass).getNoFPClass(); + FPClassTest RetTest = + RetAttrs.getAttribute(Attribute::NoFPClass).getNoFPClass(); AttributeSet RetAttrs = FAttrs.getRetAttrs(); // Only Nan and Inf tests are representable in SPIR-V now. bool ToAddNoNan = RetTest & fcNan; From f5626156b596dc31ea92f77a7600f2c02e917c98 Mon Sep 17 00:00:00 2001 From: "Sidorov, Dmitry" Date: Fri, 18 Oct 2024 07:41:07 -0700 Subject: [PATCH 3/3] Apply the suggestion Signed-off-by: Sidorov, Dmitry --- lib/SPIRV/SPIRVWriter.cpp | 5 +++- test/transcoding/fast-math-opencl-builtins.ll | 24 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index cea4e2d1b1..1b8697541d 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -3084,8 +3084,11 @@ bool LLVMToSPIRVBase::transDecoration(Value *V, SPIRVValue *BV) { bool ToAddNoNan = RetTest & fcNan; bool ToAddNoInf = RetTest & fcInf; if (ToAddNoNan || ToAddNoInf) { - const size_t NumParams = F->getFunctionType()->getNumParams(); + const auto *FT = F->getFunctionType(); + const size_t NumParams = FT->getNumParams(); for (size_t I = 0; I != NumParams; ++I) { + if (!FT->getParamType(I)->isFloatTy()) + continue; if (!F->hasParamAttribute(I, Attribute::NoFPClass)) { ToAddNoNan = false; ToAddNoInf = false; diff --git a/test/transcoding/fast-math-opencl-builtins.ll b/test/transcoding/fast-math-opencl-builtins.ll index ce0e672b7a..73ec3ee3e8 100644 --- a/test/transcoding/fast-math-opencl-builtins.ll +++ b/test/transcoding/fast-math-opencl-builtins.ll @@ -9,19 +9,23 @@ ; CHECK-SPIRV: Decorate [[#FPDec1:]] FPFastMathMode 3 ; CHECK-SPIRV: Decorate [[#FPDec2:]] FPFastMathMode 2 -; CHECK-SPIRV: Decorate [[#FPDec3:]] FPFastMathMode 16 +; CHECK-SPIRV: Decorate [[#FPDec3:]] FPFastMathMode 3 +; CHECK-SPIRV: Decorate [[#FPDec4:]] FPFastMathMode 16 ; CHECK-SPIRV: ExtInst [[#]] [[#FPDec1]] [[#]] fmax [[#]] [[#]] ; CHECK-SPIRV: ExtInst [[#]] [[#FPDec2]] [[#]] fmin [[#]] [[#]] -; CHECK-SPIRV: ExtInst [[#]] [[#FPDec3]] [[#]] fmax [[#]] [[#]] +; CHECK-SPIRV: ExtInst [[#]] [[#FPDec3]] [[#]] ldexp [[#]] [[#]] +; CHECK-SPIRV: ExtInst [[#]] [[#FPDec4]] [[#]] fmax [[#]] [[#]] ; CHECK-SPIRV-NEG-NOT: Decorate [[#]] FPFastMathMode [[#]] ; CHECK-LLVM-OCL: call nnan ninf spir_func float @_Z4fmaxff(float %[[#]], float %[[#]]) ; CHECK-LLVM-OCL: call ninf spir_func float @_Z4fminff(float %[[#]], float %[[#]]) +; CHECK-LLVM-OCL: call nnan ninf spir_func float @_Z5ldexpfi(float %[[#]], i32 %[[#]]) ; CHECK-LLVM-OCL: call fast spir_func float @_Z4fmaxff(float %[[#]], float %[[#]]) ; CHECK-LLVM-SPV: call nnan ninf spir_func float @_Z16__spirv_ocl_fmaxff(float %[[#]], float %[[#]]) ; CHECK-LLVM-SPV: call ninf spir_func float @_Z16__spirv_ocl_fminff(float %[[#]], float %[[#]]) +; CHECK-LLVM-SPV: call nnan ninf spir_func float @_Z17__spirv_ocl_ldexpfi(float %[[#]], i32 %[[#]]) ; CHECK-LLVM-SPV: call fast spir_func float @_Z16__spirv_ocl_fmaxff(float %[[#]], float %[[#]]) ; ModuleID = 'test.bc' @@ -34,6 +38,8 @@ target triple = "spir64-unknown-unknown" declare dso_local spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fmaxff(float noundef nofpclass(nan inf), float noundef nofpclass(nan inf)) local_unnamed_addr declare dso_local spir_func noundef nofpclass(nan inf) float @_Z16__spirv_ocl_fminff(float noundef nofpclass(inf), float noundef nofpclass(nan inf)) local_unnamed_addr + +declare dso_local spir_func noundef nofpclass(nan inf) float @_Z17__spirv_ocl_ldexpfi(float noundef nofpclass(nan inf), i32 noundef) define weak_odr dso_local spir_kernel void @nofpclass_all(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { entry: @@ -63,6 +69,20 @@ entry: ret void } +define weak_odr dso_local spir_kernel void @nofpclass_int(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { +entry: + %0 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId, align 32 + %arrayidx.i = getelementptr inbounds float, ptr addrspace(1) %_arg_data, i64 %0 + %arrayidx3.i = getelementptr inbounds float, ptr addrspace(1) %_arg_dat1, i64 %0 + %cmp.i = icmp ult i64 %0, 2147483648 + %arrayidx5.i = getelementptr inbounds i32, ptr addrspace(1) %_arg_dat2, i64 %0 + %1 = load float, ptr addrspace(1) %arrayidx3.i, align 4 + %2 = load i32, ptr addrspace(1) %arrayidx5.i, align 4 + %call.i.i = tail call spir_func noundef nofpclass(nan inf) float @_Z17__spirv_ocl_ldexpfi(float noundef nofpclass(inf) %1, i32 noundef %2) + store float %call.i.i, ptr addrspace(1) %arrayidx.i, align 4 + ret void +} + define weak_odr dso_local spir_kernel void @nofpclass_fast(ptr addrspace(1) noundef align 4 %_arg_data, ptr addrspace(1) noundef align 4 %_arg_dat1, ptr addrspace(1) noundef align 4 %_arg_dat2) local_unnamed_addr { entry: %0 = load i64, ptr addrspace(1) @__spirv_BuiltInGlobalInvocationId, align 32