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
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 llvm/docs/SPIRVUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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

- 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]`
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsSPIRV.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>;
}
4 changes: 3 additions & 1 deletion llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(LLVMContext::MD_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,
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
22 changes: 21 additions & 1 deletion llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,18 +820,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(),
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
; 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]]

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}