The @llvm.canonicalize intrinsic is not supported by the bi-directional translator or the LLVM SPIR-V backend.
This is a basic intrinsic that is very useful in floating point math libraries, and ideally all LLVM backend should support it. LLVM's libclc uses it extensively for targets other than SPIR-V, having SPIR-V support the instruction would simplify the library code.
I've attempted an implementation in llvm/llvm-project#178439, but I've run into issues. I would like to ask for help on what might be a reasonable lowering. The options I considered:
Option 1
Lower @llvm.canonicalize(x) -> OpFMul / OpVectorTimesScalar x, 1.0. Motivated by the sentence in the LLVM language reference "This function should always be implementable as multiplication by 1.0".
This is the approach taken in llvm/llvm-project#178439. According to my understanding of the spec however, the SPIR-V consumer is allowed to optimize this away.
This happens in practice, the reverse translation of OpFMul x, 1.0 is fmul x, 1.0, which is then folded by LLVM middle-end optimization passes (InstSimplify).
This could be worked around by special-casing OpFMul x, 1.0 to translate to @llvm.canonicalize, thereby preserving the semantics. OpFMul x, 1.0 could theoretically show up naturally in IR, not just from llvm.canonicalize, in which case we'd be pessimizing code. It also does not address SPIR-V consumers that do not translate to LLVM-IR.
Option 2
Lower to a different "identity" floating point instruction, hopefully one that is not folded out by LLVM transformations. For example fmin x, x from the OpenCL extended instruction set. Using these might hurt performance: the reverse translation is to call a function fmin or __spirv_fmin, this is going to optimize much worse than llvm.canonicalize.
Option 3
Explicitly support floating point canonicalization instruction in SPIR-V via an extension. This would be the cleanest approach. The semantics of such an instruction would have to be defined for what makes sense for SPIR-V.
The
@llvm.canonicalizeintrinsic is not supported by the bi-directional translator or the LLVM SPIR-V backend.This is a basic intrinsic that is very useful in floating point math libraries, and ideally all LLVM backend should support it. LLVM's libclc uses it extensively for targets other than SPIR-V, having SPIR-V support the instruction would simplify the library code.
I've attempted an implementation in llvm/llvm-project#178439, but I've run into issues. I would like to ask for help on what might be a reasonable lowering. The options I considered:
Option 1
Lower
@llvm.canonicalize(x)->OpFMul / OpVectorTimesScalar x, 1.0. Motivated by the sentence in the LLVM language reference "This function should always be implementable as multiplication by 1.0".This is the approach taken in llvm/llvm-project#178439. According to my understanding of the spec however, the SPIR-V consumer is allowed to optimize this away.
This happens in practice, the reverse translation of
OpFMul x, 1.0isfmul x, 1.0, which is then folded by LLVM middle-end optimization passes (InstSimplify).This could be worked around by special-casing
OpFMul x, 1.0to translate to@llvm.canonicalize, thereby preserving the semantics.OpFMul x, 1.0could theoretically show up naturally in IR, not just from llvm.canonicalize, in which case we'd be pessimizing code. It also does not address SPIR-V consumers that do not translate to LLVM-IR.Option 2
Lower to a different "identity" floating point instruction, hopefully one that is not folded out by LLVM transformations. For example
fmin x, xfrom the OpenCL extended instruction set. Using these might hurt performance: the reverse translation is to call a functionfminor__spirv_fmin, this is going to optimize much worse thanllvm.canonicalize.Option 3
Explicitly support floating point canonicalization instruction in SPIR-V via an extension. This would be the cleanest approach. The semantics of such an instruction would have to be defined for what makes sense for SPIR-V.