Skip to content

[SPIR-V] Support SPV_INTEL_fp_max_error extension for !fpmath metadata #130619

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

Merged
merged 5 commits into from
Mar 14, 2025

Conversation

vmaksimo
Copy link
Contributor

@llvmbot
Copy link
Member

llvmbot commented Mar 10, 2025

@llvm/pr-subscribers-backend-spir-v

Author: Viktoria Maximova (vmaksimo)

Changes

Specification:
https://github.khronos.org/SPIRV-Registry/extensions/INTEL/SPV_INTEL_fp_max_error.html


Full diff: https://github.com/llvm/llvm-project/pull/130619.diff

8 Files Affected:

  • (modified) llvm/docs/SPIRVUsage.rst (+6)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+2)
  • (modified) llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp (+3-1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+13)
  • (modified) llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp (+3)
  • (modified) llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp (+21-1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td (+3)
  • (added) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll (+35)
diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 6a27e0efaec7f..ee5937f208825 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -207,6 +207,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
      - Allows support for additional group operations within uniform control flow.
    * - ``SPV_KHR_non_semantic_info``
      - Adds the ability to declare extended instruction sets that have no semantic impact and can be safely removed from a module.
+   * - ``SPV_INTEL_fp_max_error``
+     - Adds the ability to specify the maximum error for floating-point operations.
 
 To enable multiple extensions, list them separated by comma. For example, to enable support for atomic operations on floating-point numbers and arbitrary precision integers, use:
 
@@ -307,6 +309,10 @@ SPIR-V backend, along with their descriptions and argument details.
      - None
      - `[Type, 32-bit Integer, Metadata]`
      - Assigns one of two memory aliasing decorations (specified by the second argument) to instructions using original aliasing metadata node. Not emitted directly but used to support SPIR-V representation in LLVM IR.
+   * - `int_spv_assign_fpmaxerror_decoration`
+      - None
+      - `[Type, Metadata]`
+      - Assigns the maximum error decoration to floating-point instructions using the original metadata node. Not emitted directly but used to support SPIR-V representation in LLVM IR.
    * - `int_spv_track_constant`
      - Type
      - `[Type, Metadata]`
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 7012ef3534c68..df3e137c80980 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -141,4 +141,6 @@ let TargetPrefix = "spv" in {
   // Memory aliasing intrinsics
   def int_spv_assign_aliasing_decoration : Intrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_metadata_ty], [ImmArg<ArgIndex<1>>]>;
 
+  // FPMaxErrorDecorationINTEL
+  def int_spv_assign_fpmaxerror_decoration: Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index 8d1714932c3c6..37119bf01545c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -90,7 +90,9 @@ static const std::map<std::string, SPIRV::Extension::Extension, std::less<>>
         {"SPV_KHR_non_semantic_info",
          SPIRV::Extension::Extension::SPV_KHR_non_semantic_info},
         {"SPV_INTEL_long_composites",
-         SPIRV::Extension::Extension::SPV_INTEL_long_composites}};
+         SPIRV::Extension::Extension::SPV_INTEL_long_composites},
+        {"SPV_INTEL_fp_max_error",
+         SPIRV::Extension::Extension::SPV_INTEL_fp_max_error}};
 
 bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
                                   llvm::StringRef ArgValue,
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 751ea5ab2dc47..00acb6aa19c9c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2016,6 +2016,19 @@ void SPIRVEmitIntrinsics::insertSpirvDecorations(Instruction *I,
     processMemAliasingDecoration(LLVMContext::MD_alias_scope);
     processMemAliasingDecoration(LLVMContext::MD_noalias);
   }
+  // MD_fpmath
+  if (MDNode *MD = I->getMetadata("fpmath")) {
+    const SPIRVSubtarget *STI = TM->getSubtargetImpl(*I->getFunction());
+    bool AllowFPMaxError =
+        STI->canUseExtension(SPIRV::Extension::SPV_INTEL_fp_max_error);
+    if (!AllowFPMaxError)
+      return;
+
+    setInsertPointAfterDef(B, I);
+    B.CreateIntrinsic(Intrinsic::spv_assign_fpmaxerror_decoration,
+                      {I->getType()},
+                      {I, MetadataAsValue::get(I->getContext(), MD)});
+  }
 }
 
 void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index e0b348f0bba10..63894acacbc73 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -888,6 +888,9 @@ static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
         SPIRV::Extension::SPV_INTEL_global_variable_fpga_decorations);
   } else if (Dec == SPIRV::Decoration::NonUniformEXT) {
     Reqs.addRequirements(SPIRV::Capability::ShaderNonUniformEXT);
+  } else if (Dec == SPIRV::Decoration::FPMaxErrorDecorationINTEL) {
+    Reqs.addRequirements(SPIRV::Capability::FPMaxErrorINTEL);
+    Reqs.addExtension(SPIRV::Extension::SPV_INTEL_fp_max_error);
   }
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index baacd58b28028..4904506f25a08 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -819,18 +819,38 @@ static void insertInlineAsm(MachineFunction &MF, SPIRVGlobalRegistry *GR,
   insertInlineAsmProcess(MF, GR, ST, MIRBuilder, ToProcess);
 }
 
+static uint32_t convertFloatToSPIRVWord(float F) {
+  union {
+    float F;
+    uint32_t Spir;
+  } FPMaxError;
+  FPMaxError.F = F;
+  return FPMaxError.Spir;
+}
+
 static void insertSpirvDecorations(MachineFunction &MF, SPIRVGlobalRegistry *GR,
                                    MachineIRBuilder MIB) {
   SmallVector<MachineInstr *, 10> ToErase;
   for (MachineBasicBlock &MBB : MF) {
     for (MachineInstr &MI : MBB) {
       if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_decoration) &&
-          !isSpvIntrinsic(MI, Intrinsic::spv_assign_aliasing_decoration))
+          !isSpvIntrinsic(MI, Intrinsic::spv_assign_aliasing_decoration) &&
+          !isSpvIntrinsic(MI, Intrinsic::spv_assign_fpmaxerror_decoration))
         continue;
       MIB.setInsertPt(*MI.getParent(), MI.getNextNode());
       if (isSpvIntrinsic(MI, Intrinsic::spv_assign_decoration)) {
         buildOpSpirvDecorations(MI.getOperand(1).getReg(), MIB,
                                 MI.getOperand(2).getMetadata());
+      } else if (isSpvIntrinsic(MI,
+                                Intrinsic::spv_assign_fpmaxerror_decoration)) {
+        ConstantFP *OpV = mdconst::dyn_extract<ConstantFP>(
+            MI.getOperand(2).getMetadata()->getOperand(0));
+        uint32_t OpValue =
+            convertFloatToSPIRVWord(OpV->getValueAPF().convertToFloat());
+
+        buildOpDecorate(MI.getOperand(1).getReg(), MIB,
+                        SPIRV::Decoration::FPMaxErrorDecorationINTEL,
+                        {OpValue});
       } else {
         GR->buildMemAliasingOpDecorate(MI.getOperand(1).getReg(), MIB,
                                        MI.getOperand(2).getImm(),
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index a871518e2094c..caee778eddbc4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -312,6 +312,7 @@ defm SPV_INTEL_float_controls2 : ExtensionOperand<115>;
 defm SPV_INTEL_bindless_images : ExtensionOperand<116>;
 defm SPV_INTEL_long_composites : ExtensionOperand<117>;
 defm SPV_INTEL_memory_access_aliasing : ExtensionOperand<118>;
+defm SPV_INTEL_fp_max_error : ExtensionOperand<119>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define Capabilities enum values and at the same time
@@ -511,6 +512,7 @@ defm FunctionFloatControlINTEL : CapabilityOperand<5821, 0, 0, [SPV_INTEL_float_
 defm LongCompositesINTEL : CapabilityOperand<6089, 0, 0, [SPV_INTEL_long_composites], []>;
 defm BindlessImagesINTEL : CapabilityOperand<6528, 0, 0, [SPV_INTEL_bindless_images], []>;
 defm MemoryAccessAliasingINTEL : CapabilityOperand<5910, 0, 0, [SPV_INTEL_memory_access_aliasing], []>;
+defm FPMaxErrorINTEL : CapabilityOperand<6169, 0, 0, [SPV_INTEL_fp_max_error], []>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define SourceLanguage enum values and at the same time
@@ -1261,6 +1263,7 @@ defm FunctionDenormModeINTEL : DecorationOperand<5823, 0, 0, [], [FunctionFloatC
 defm FunctionFloatingPointModeINTEL : DecorationOperand<6080, 0, 0, [], [FunctionFloatControlINTEL]>;
 defm AliasScopeINTEL : DecorationOperand<5914, 0, 0, [], [MemoryAccessAliasingINTEL]>;
 defm NoAliasINTEL : DecorationOperand<5915, 0, 0, [], [MemoryAccessAliasingINTEL]>;
+defm FPMaxErrorDecorationINTEL : DecorationOperand<6170, 0, 0, [], [FPMaxErrorINTEL]>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define BuiltIn enum values and at the same time
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
new file mode 100644
index 0000000000000..66070181968e2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
@@ -0,0 +1,35 @@
+; Confirm that we handle fpmath metadata correctly
+; This is a copy of https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/test/extensions/INTEL/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_fp_max_error %s -o %t.spt
+; RUN: FileCheck %s --input-file=%t.spt
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_fp_max_error %s -o - -filetype=obj | spirv-val %}
+
+; CHECK: OpCapability FPMaxErrorINTEL
+; CHECK: OpExtension "SPV_INTEL_fp_max_error"
+
+; CHECK: OpName %[[#CalleeName:]] "callee"
+; CHECK: OpName %[[#F3:]] "f3"
+; CHECK: OpDecorate %[[#F3]] FPMaxErrorDecorationINTEL 1075838976
+; CHECK: OpDecorate %[[#Callee:]] FPMaxErrorDecorationINTEL 1065353216
+
+; CHECK: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK: %[[#Callee]] = OpFunctionCall %[[#FloatTy]] %[[#CalleeName]]
+
+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"
+
+define float @callee(float %f1, float %f2) {
+entry:
+ret float %f1
+}
+
+define void @test_fp_max_error_decoration(float %f1, float %f2) {
+entry:
+%f3 = fdiv float %f1, %f2, !fpmath !0
+call float @callee(float %f1, float %f2), !fpmath !1
+ret void
+}
+
+!0 = !{float 2.500000e+00}
+!1 = !{float 1.000000e+00}

@llvmbot
Copy link
Member

llvmbot commented Mar 10, 2025

@llvm/pr-subscribers-llvm-ir

Author: Viktoria Maximova (vmaksimo)

Changes

Specification:
https://github.khronos.org/SPIRV-Registry/extensions/INTEL/SPV_INTEL_fp_max_error.html


Full diff: https://github.com/llvm/llvm-project/pull/130619.diff

8 Files Affected:

  • (modified) llvm/docs/SPIRVUsage.rst (+6)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+2)
  • (modified) llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp (+3-1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp (+13)
  • (modified) llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp (+3)
  • (modified) llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp (+21-1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td (+3)
  • (added) llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll (+35)
diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index 6a27e0efaec7f..ee5937f208825 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -207,6 +207,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
      - Allows support for additional group operations within uniform control flow.
    * - ``SPV_KHR_non_semantic_info``
      - Adds the ability to declare extended instruction sets that have no semantic impact and can be safely removed from a module.
+   * - ``SPV_INTEL_fp_max_error``
+     - Adds the ability to specify the maximum error for floating-point operations.
 
 To enable multiple extensions, list them separated by comma. For example, to enable support for atomic operations on floating-point numbers and arbitrary precision integers, use:
 
@@ -307,6 +309,10 @@ SPIR-V backend, along with their descriptions and argument details.
      - None
      - `[Type, 32-bit Integer, Metadata]`
      - Assigns one of two memory aliasing decorations (specified by the second argument) to instructions using original aliasing metadata node. Not emitted directly but used to support SPIR-V representation in LLVM IR.
+   * - `int_spv_assign_fpmaxerror_decoration`
+      - None
+      - `[Type, Metadata]`
+      - Assigns the maximum error decoration to floating-point instructions using the original metadata node. Not emitted directly but used to support SPIR-V representation in LLVM IR.
    * - `int_spv_track_constant`
      - Type
      - `[Type, Metadata]`
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 7012ef3534c68..df3e137c80980 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -141,4 +141,6 @@ let TargetPrefix = "spv" in {
   // Memory aliasing intrinsics
   def int_spv_assign_aliasing_decoration : Intrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_metadata_ty], [ImmArg<ArgIndex<1>>]>;
 
+  // FPMaxErrorDecorationINTEL
+  def int_spv_assign_fpmaxerror_decoration: Intrinsic<[], [llvm_any_ty, llvm_metadata_ty]>;
 }
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index 8d1714932c3c6..37119bf01545c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -90,7 +90,9 @@ static const std::map<std::string, SPIRV::Extension::Extension, std::less<>>
         {"SPV_KHR_non_semantic_info",
          SPIRV::Extension::Extension::SPV_KHR_non_semantic_info},
         {"SPV_INTEL_long_composites",
-         SPIRV::Extension::Extension::SPV_INTEL_long_composites}};
+         SPIRV::Extension::Extension::SPV_INTEL_long_composites},
+        {"SPV_INTEL_fp_max_error",
+         SPIRV::Extension::Extension::SPV_INTEL_fp_max_error}};
 
 bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
                                   llvm::StringRef ArgValue,
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 751ea5ab2dc47..00acb6aa19c9c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -2016,6 +2016,19 @@ void SPIRVEmitIntrinsics::insertSpirvDecorations(Instruction *I,
     processMemAliasingDecoration(LLVMContext::MD_alias_scope);
     processMemAliasingDecoration(LLVMContext::MD_noalias);
   }
+  // MD_fpmath
+  if (MDNode *MD = I->getMetadata("fpmath")) {
+    const SPIRVSubtarget *STI = TM->getSubtargetImpl(*I->getFunction());
+    bool AllowFPMaxError =
+        STI->canUseExtension(SPIRV::Extension::SPV_INTEL_fp_max_error);
+    if (!AllowFPMaxError)
+      return;
+
+    setInsertPointAfterDef(B, I);
+    B.CreateIntrinsic(Intrinsic::spv_assign_fpmaxerror_decoration,
+                      {I->getType()},
+                      {I, MetadataAsValue::get(I->getContext(), MD)});
+  }
 }
 
 void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I,
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index e0b348f0bba10..63894acacbc73 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -888,6 +888,9 @@ static void addOpDecorateReqs(const MachineInstr &MI, unsigned DecIndex,
         SPIRV::Extension::SPV_INTEL_global_variable_fpga_decorations);
   } else if (Dec == SPIRV::Decoration::NonUniformEXT) {
     Reqs.addRequirements(SPIRV::Capability::ShaderNonUniformEXT);
+  } else if (Dec == SPIRV::Decoration::FPMaxErrorDecorationINTEL) {
+    Reqs.addRequirements(SPIRV::Capability::FPMaxErrorINTEL);
+    Reqs.addExtension(SPIRV::Extension::SPV_INTEL_fp_max_error);
   }
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index baacd58b28028..4904506f25a08 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -819,18 +819,38 @@ static void insertInlineAsm(MachineFunction &MF, SPIRVGlobalRegistry *GR,
   insertInlineAsmProcess(MF, GR, ST, MIRBuilder, ToProcess);
 }
 
+static uint32_t convertFloatToSPIRVWord(float F) {
+  union {
+    float F;
+    uint32_t Spir;
+  } FPMaxError;
+  FPMaxError.F = F;
+  return FPMaxError.Spir;
+}
+
 static void insertSpirvDecorations(MachineFunction &MF, SPIRVGlobalRegistry *GR,
                                    MachineIRBuilder MIB) {
   SmallVector<MachineInstr *, 10> ToErase;
   for (MachineBasicBlock &MBB : MF) {
     for (MachineInstr &MI : MBB) {
       if (!isSpvIntrinsic(MI, Intrinsic::spv_assign_decoration) &&
-          !isSpvIntrinsic(MI, Intrinsic::spv_assign_aliasing_decoration))
+          !isSpvIntrinsic(MI, Intrinsic::spv_assign_aliasing_decoration) &&
+          !isSpvIntrinsic(MI, Intrinsic::spv_assign_fpmaxerror_decoration))
         continue;
       MIB.setInsertPt(*MI.getParent(), MI.getNextNode());
       if (isSpvIntrinsic(MI, Intrinsic::spv_assign_decoration)) {
         buildOpSpirvDecorations(MI.getOperand(1).getReg(), MIB,
                                 MI.getOperand(2).getMetadata());
+      } else if (isSpvIntrinsic(MI,
+                                Intrinsic::spv_assign_fpmaxerror_decoration)) {
+        ConstantFP *OpV = mdconst::dyn_extract<ConstantFP>(
+            MI.getOperand(2).getMetadata()->getOperand(0));
+        uint32_t OpValue =
+            convertFloatToSPIRVWord(OpV->getValueAPF().convertToFloat());
+
+        buildOpDecorate(MI.getOperand(1).getReg(), MIB,
+                        SPIRV::Decoration::FPMaxErrorDecorationINTEL,
+                        {OpValue});
       } else {
         GR->buildMemAliasingOpDecorate(MI.getOperand(1).getReg(), MIB,
                                        MI.getOperand(2).getImm(),
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index a871518e2094c..caee778eddbc4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -312,6 +312,7 @@ defm SPV_INTEL_float_controls2 : ExtensionOperand<115>;
 defm SPV_INTEL_bindless_images : ExtensionOperand<116>;
 defm SPV_INTEL_long_composites : ExtensionOperand<117>;
 defm SPV_INTEL_memory_access_aliasing : ExtensionOperand<118>;
+defm SPV_INTEL_fp_max_error : ExtensionOperand<119>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define Capabilities enum values and at the same time
@@ -511,6 +512,7 @@ defm FunctionFloatControlINTEL : CapabilityOperand<5821, 0, 0, [SPV_INTEL_float_
 defm LongCompositesINTEL : CapabilityOperand<6089, 0, 0, [SPV_INTEL_long_composites], []>;
 defm BindlessImagesINTEL : CapabilityOperand<6528, 0, 0, [SPV_INTEL_bindless_images], []>;
 defm MemoryAccessAliasingINTEL : CapabilityOperand<5910, 0, 0, [SPV_INTEL_memory_access_aliasing], []>;
+defm FPMaxErrorINTEL : CapabilityOperand<6169, 0, 0, [SPV_INTEL_fp_max_error], []>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define SourceLanguage enum values and at the same time
@@ -1261,6 +1263,7 @@ defm FunctionDenormModeINTEL : DecorationOperand<5823, 0, 0, [], [FunctionFloatC
 defm FunctionFloatingPointModeINTEL : DecorationOperand<6080, 0, 0, [], [FunctionFloatControlINTEL]>;
 defm AliasScopeINTEL : DecorationOperand<5914, 0, 0, [], [MemoryAccessAliasingINTEL]>;
 defm NoAliasINTEL : DecorationOperand<5915, 0, 0, [], [MemoryAccessAliasingINTEL]>;
+defm FPMaxErrorDecorationINTEL : DecorationOperand<6170, 0, 0, [], [FPMaxErrorINTEL]>;
 
 //===----------------------------------------------------------------------===//
 // Multiclass used to define BuiltIn enum values and at the same time
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
new file mode 100644
index 0000000000000..66070181968e2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
@@ -0,0 +1,35 @@
+; Confirm that we handle fpmath metadata correctly
+; This is a copy of https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/test/extensions/INTEL/SPV_INTEL_fp_max_error/IntelFPMaxErrorFPMath.ll
+
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_fp_max_error %s -o %t.spt
+; RUN: FileCheck %s --input-file=%t.spt
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_INTEL_fp_max_error %s -o - -filetype=obj | spirv-val %}
+
+; CHECK: OpCapability FPMaxErrorINTEL
+; CHECK: OpExtension "SPV_INTEL_fp_max_error"
+
+; CHECK: OpName %[[#CalleeName:]] "callee"
+; CHECK: OpName %[[#F3:]] "f3"
+; CHECK: OpDecorate %[[#F3]] FPMaxErrorDecorationINTEL 1075838976
+; CHECK: OpDecorate %[[#Callee:]] FPMaxErrorDecorationINTEL 1065353216
+
+; CHECK: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK: %[[#Callee]] = OpFunctionCall %[[#FloatTy]] %[[#CalleeName]]
+
+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"
+
+define float @callee(float %f1, float %f2) {
+entry:
+ret float %f1
+}
+
+define void @test_fp_max_error_decoration(float %f1, float %f2) {
+entry:
+%f3 = fdiv float %f1, %f2, !fpmath !0
+call float @callee(float %f1, float %f2), !fpmath !1
+ret void
+}
+
+!0 = !{float 2.500000e+00}
+!1 = !{float 1.000000e+00}

@@ -307,6 +309,10 @@ SPIR-V backend, along with their descriptions and argument details.
- None
- `[Type, 32-bit Integer, Metadata]`
- Assigns one of two memory aliasing decorations (specified by the second argument) to instructions using original aliasing metadata node. Not emitted directly but used to support SPIR-V representation in LLVM IR.
* - `int_spv_assign_fpmaxerror_decoration`
Copy link
Contributor

@MrSidims MrSidims Mar 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to create a new intrinsic? I have a feeling, that this functionality can be added in handleMIFlagDecoration function. (if it's possible) by casting instruction to FPMathOperator and calling getFPAccuracy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe FP-Math flags are not the same as !fpmath metadata. I've added intrinsic to support the second case, but we can extend the extension usage to support your case too (we didn't have such support in Khronos though)

Copy link
Contributor

@MrSidims MrSidims Mar 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is, see: https://llvm.org/doxygen/Instructions_8cpp_source.html#l02682

A valid point would be: llvm ir instruction != mir instruction, so API I'm referring above is not applicable. Yet during my recent work on aliasing metadata I've found, that information from aliasing metadata is being transferred to MachineMemOperand class and I can access this information in the backend by calling getAAInfo. I'm not sure, if it will work, as I'm still a novice in LLVM backend, yet I do see MO_FPImmediate enum among MachineOperandType class. Can we check, that it (or some other machine operand subclass) doesn't store accuracy information?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea, but it seems that we can't do that - handleMIFlagDecoration is "too late" in the stack, and we don't have FP accuracy saved in BE machine instructions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll unresolve the thread just in case if other reviewers have something to say

Copy link
Contributor

@MrSidims MrSidims left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MrSidims MrSidims merged commit a58a6a9 into llvm:main Mar 14, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants