From c8c3975a12b3307702faf43a641be8b44539bc17 Mon Sep 17 00:00:00 2001 From: vishakh prakash Date: Mon, 3 Feb 2025 14:55:31 +0530 Subject: [PATCH] FEAT: - add function for adding saturation decoration to sat intrinsics - add test for fp to int intrinsic - add legalization for G_FPTOSI_SAT and G_FPTOUI_SAT - add instruction selection logic for G_FPTOSI_SAT and G_FPTOUI_SAT --- llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 22 ++ .../Target/SPIRV/SPIRVInstructionSelector.cpp | 4 + llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 4 + .../llvm-intrinsics/fp_to_int_intrinsic.ll | 220 ++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp_to_int_intrinsic.ll diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 702206b8e0dc5..cec3e2dba14e0 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -18,12 +18,15 @@ #include "SPIRVTargetMachine.h" #include "SPIRVUtils.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/InstVisitor.h" #include "llvm/IR/IntrinsicsSPIRV.h" #include "llvm/IR/TypedPointerType.h" +#include "llvm/Support/Casting.h" +#include #include #include @@ -1337,6 +1340,24 @@ static void createSaturatedConversionDecoration(Instruction *I, createDecorationIntrinsic(I, SaturatedConversionNode, B); } +static void addSaturatedDecorationToIntrinsic(Instruction *I, IRBuilder<> &B) { + // llvm::errs() << "this function is running successfully"; + if (auto *CI = dyn_cast(I)) { + Function *F = CI->getCalledFunction(); + if (F->isIntrinsic()) { + StringRef S = F->getName(); + SmallVector Parts; + S.split(Parts, ".", -1, false); + + if (Parts.size() > 1) { + if (std::find(Parts.begin(), Parts.end(), "sat") != Parts.end()) { + createSaturatedConversionDecoration(I, B); + } + } + } + } +} + Instruction *SPIRVEmitIntrinsics::visitCallInst(CallInst &Call) { if (!Call.isInlineAsm()) return &Call; @@ -2400,6 +2421,7 @@ bool SPIRVEmitIntrinsics::runOnFunction(Function &Func) { if (isConvergenceIntrinsic(I)) continue; + addSaturatedDecorationToIntrinsic(I, B); processInstrAfterVisit(I, B); } diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index e7d8fe5bd8015..49642126d34cf 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -602,6 +602,10 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg, return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToS); case TargetOpcode::G_FPTOUI: return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToU); + case TargetOpcode::G_FPTOSI_SAT: + return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToS); + case TargetOpcode::G_FPTOUI_SAT: + return selectUnOp(ResVReg, ResType, I, SPIRV::OpConvertFToU); case TargetOpcode::G_SITOFP: return selectIToF(ResVReg, ResType, I, true, SPIRV::OpConvertSToF); diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index fa5e0a80576d0..694d584c26e04 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -236,6 +236,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { .legalForCartesianProduct(allIntScalarsAndVectors, allFloatScalarsAndVectors); + getActionDefinitionsBuilder({G_FPTOSI_SAT, G_FPTOUI_SAT}) + .legalForCartesianProduct(allIntScalarsAndVectors, + allFloatScalarsAndVectors); + getActionDefinitionsBuilder({G_SITOFP, G_UITOFP}) .legalForCartesianProduct(allFloatScalarsAndVectors, allScalarsAndVectors); diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp_to_int_intrinsic.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp_to_int_intrinsic.ll new file mode 100644 index 0000000000000..1f0feee1d914c --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp_to_int_intrinsic.ll @@ -0,0 +1,220 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv64-unkown-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unkown-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK: OpDecorate %[[#SAT1:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT2:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT3:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT4:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT5:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT6:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT7:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT8:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT9:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT10:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT11:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT12:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT13:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT14:]] SaturatedConversion +; CHECK: OpDecorate %[[#SAT15:]] SaturatedConversion + + +; CHECK: %[[#SAT1:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_signed_i8(float %input) { +entry: + %ptr = alloca i8 + %0 = call i8 @llvm.fptosi.sat.i8.f32(float %input) + store i8 %0, i8* %ptr + ret void +} +declare i8 @llvm.fptosi.sat.i8.f32(float) + + +; CHECK: %[[#SAT2:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_signed_i16(float %input) { +entry: + %ptr = alloca i16 + %0 = call i16 @llvm.fptosi.sat.i16.f32(float %input) + store i16 %0, i16* %ptr + ret void +} +declare i16 @llvm.fptosi.sat.i16.f32(float) + +;;;;;;; output i32 + +; CHECK: %[[#SAT3:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_signed_i32(float %input) { +entry: + %ptr = alloca i32 + %0 = call i32 @llvm.fptosi.sat.i32.f32(float %input) + store i32 %0, i32* %ptr + ret void +} +declare i32 @llvm.fptosi.sat.i32.f32(float) + +;;;;;;; output i64 +; CHECK: %[[#SAT4:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_signed_i64(float %input) { +entry: + %ptr = alloca i64 + %0 = call i64 @llvm.fptosi.sat.i64.f32(float %input) + store i64 %0, i64* %ptr + ret void +} +declare i64 @llvm.fptosi.sat.i64.f32(float) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; double input +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;; output i8 +; CHECK: %[[#SAT5:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_signed_i8(double %input) { +entry: + %ptr = alloca i8 + %0 = call i8 @llvm.fptosi.sat.i8.f64(double %input) + store i8 %0, i8* %ptr + ret void +} +declare i8 @llvm.fptosi.sat.i8.f64(double) + + +; CHECK: %[[#SAT6:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_signed_i16(double %input) { +entry: + %ptr = alloca i16 + %0 = call i16 @llvm.fptosi.sat.i16.f64(double %input) + store i16 %0, i16* %ptr + ret void +} +declare i16 @llvm.fptosi.sat.i16.f64(double) + + +; CHECK: %[[#SAT7:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_signed_i32(double %input) { +entry: + %ptr = alloca i32 + %0 = call i32 @llvm.fptosi.sat.i32.f64(double %input) + store i32 %0, i32* %ptr + ret void +} +declare i32 @llvm.fptosi.sat.i32.f64(double) + + +; CHECK: %[[#SAT8:]] = OpConvertFToS %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_signed_i64(double %input) { +entry: + %ptr = alloca i64 + %0 = call i64 @llvm.fptosi.sat.i64.f64(double %input) + store i64 %0, i64* %ptr + ret void +} +declare i64 @llvm.fptosi.sat.i64.f64(double) + + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; unsigned output +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; float input +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;; output i8 +; CHECK: %[[#SAT8:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_unsigned_i8(float %input) { +entry: + %ptr = alloca i8 + %0 = call i8 @llvm.fptoui.sat.i8.f32(float %input) + store i8 %0, i8* %ptr + ret void +} +declare i8 @llvm.fptoui.sat.i8.f32(float) + +;;;;;;; output i16 +; CHECK: %[[#SAT9:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_unsigned_i16(float %input) { +entry: + %ptr = alloca i16 + %0 = call i16 @llvm.fptoui.sat.i16.f32(float %input) + store i16 %0, i16* %ptr + ret void +} +declare i16 @llvm.fptoui.sat.i16.f32(float) + +;;;;;;; output i32 +; CHECK: %[[#SAT10:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_unsigned_i32(float %input) { +entry: + %ptr = alloca i32 + %0 = call i32 @llvm.fptoui.sat.i32.f32(float %input) + store i32 %0, i32* %ptr + ret void +} +declare i32 @llvm.fptoui.sat.i32.f32(float) + +;;;;;;; output i64 +; CHECK: %[[#SAT11:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_float_to_unsigned_i64(float %input) { +entry: + %ptr = alloca i64 + %0 = call i64 @llvm.fptoui.sat.i64.f32(float %input) + store i64 %0, i64* %ptr + ret void +} +declare i64 @llvm.fptoui.sat.i64.f32(float) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; double input +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;;;;;; output i8 +; CHECK: %[[#SAT12:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_unsigned_i8(double %input) { +entry: + %ptr = alloca i8 + %0 = call i8 @llvm.fptoui.sat.i8.f64(double %input) + store i8 %0, i8* %ptr + ret void +} +declare i8 @llvm.fptoui.sat.i8.f64(double) + +;;;;;;; output i16 +; CHECK: %[[#SAT13:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_unsigned_i16(double %input) { +entry: + %ptr = alloca i16 + %0 = call i16 @llvm.fptoui.sat.i16.f64(double %input) + store i16 %0, i16* %ptr + ret void +} +declare i16 @llvm.fptoui.sat.i16.f64(double) + +;;;;;;; output i32 +; CHECK: %[[#SAT14:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_unsigned_i32(double %input) { +entry: + %ptr = alloca i32 + %0 = call i32 @llvm.fptoui.sat.i32.f64(double %input) + store i32 %0, i32* %ptr + ret void +} +declare i32 @llvm.fptoui.sat.i32.f64(double) + +;;;;;;; output i64 +; CHECK: %[[#SAT15:]] = OpConvertFToU %{{[0-9]+}} %[[#]] +define spir_kernel void @testfunction_double_to_unsigned_i64(double %input) { +entry: + %ptr = alloca i64 + %0 = call i64 @llvm.fptoui.sat.i64.f64(double %input) + store i64 %0, i64* %ptr + ret void +} +declare i64 @llvm.fptoui.sat.i64.f64(double) + + + +