A module using OpUMulExtended fails to build on every IGC I have tested:
$ ocloc compile -file umulo.spv -spirv_input -device pvc
error: undefined reference to `_Z20__spirv_UMulExtendedll'
in function: '__spirv_UMulExtended(long, long)' called by kernel: 'k'
error: backend compiler failed build.
The module is spirv-val clean and OpUMulExtended is core SPIR-V 1.0, no extension involved.
The SPIR-V reader mangles the operands as signed, _Z20__spirv_UMulExtendedll, but IGC/BiFModule/Implementation/arithmetic.cl defines only the unsigned overloads, __spirv_UMulExtended(ulong Operand1, ulong Operand2) and friends, which mangle to _Z20__spirv_UMulExtendedmm. There is no signed-typed __spirv_UMulExtended in that file. The same module with OpSMulExtended instead builds fine, because there the signed mangling matches __spirv_SMulExtended(long, long).
Tested:
Intel Data Center GPU Max 1550, IGC 2.11.29 (agama 25.18), ocloc -device pvc : fails
Intel Arc B570, IGC 2.38.2, ocloc -device bmg : fails
This looks fixed upstream by KhronosGroup/SPIRV-LLVM-Translator#3963 (merged 2026-08-18), which adds OpUMulExtended to the opcodes whose builtin arguments are mangled unsigned. That commit is on main only; it is not on llvm_release_170, which is what external/llvm/llvm_preferred_version.cmake points IGC at today, so no released IGC picks it up. Would you take a backport of that into the translator release branch IGC builds against, or alternatively add the signed overloads to the BiF module?
Reproducer, as SPIR-V assembly for spirv-as:
OpCapability Kernel
OpCapability Addresses
OpCapability Int64
%1 = OpExtInstImport "OpenCL.std"
OpMemoryModel Physical64 OpenCL
OpEntryPoint Kernel %15 "k"
OpName %11 "lo"
OpName %12 "ov"
%3 = OpTypeInt 64 0
%4 = OpTypePointer CrossWorkgroup %3
%5 = OpTypeVoid
%6 = OpTypeFunction %5 %4 %4 %3 %3
%7 = OpTypeBool
%8 = OpConstantNull %3
%9 = OpConstant %3 1
%10 = OpTypeStruct %3 %3
%15 = OpFunction %5 None %6
%11 = OpFunctionParameter %4
%12 = OpFunctionParameter %4
%13 = OpFunctionParameter %3
%14 = OpFunctionParameter %3
%2 = OpLabel
%16 = OpUMulExtended %10 %13 %14
%17 = OpCompositeExtract %3 %16 0
%18 = OpCompositeExtract %3 %16 1
%19 = OpINotEqual %7 %18 %8
%20 = OpSelect %3 %19 %9 %8
OpStore %11 %17 Aligned 8
OpStore %12 %20 Aligned 8
OpReturn
OpFunctionEnd
The same SPIR-V comes out of the in-tree LLVM SPIR-V backend for this input, so any language frontend that emits llvm.umul.with.overflow hits it:
define spir_kernel void @k(ptr addrspace(1) %lo, ptr addrspace(1) %ov, i64 %a, i64 %b) {
entry:
%r = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %a, i64 %b)
%v = extractvalue { i64, i1 } %r, 0
%o = extractvalue { i64, i1 } %r, 1
%oz = zext i1 %o to i64
store i64 %v, ptr addrspace(1) %lo
store i64 %oz, ptr addrspace(1) %ov
ret void
}
llc -mtriple=spirv64-unknown-unknown umulo.ll -filetype=obj -o umulo.spv
Impact for us: clang emits llvm.umul.with.overflow.i64 for a C++ new T[n] in device code, so any HIP or SYCL kernel that news an array fails to build. In chipStar all device code of a binary lands in one module, so the one unresolved symbol fails every kernel in the program.
Update: this is about to matter on a second path as well. KhronosGroup/SPIRV-LLVM-Translator#3977 changes llvm-spirv to lower llvm.umul.with.overflow to OpUMulExtended directly, instead of the spirv.llvm_umul_with_overflow_* helper function it emits today. That helper is the only reason SPIR-V produced by the translator still builds on a released IGC. If #3977 lands before IGC picks up #3963, modules coming from the translator will carry OpUMulExtended too, and hit the same undefined reference.
A module using
OpUMulExtendedfails to build on every IGC I have tested:The module is
spirv-valclean andOpUMulExtendedis core SPIR-V 1.0, no extension involved.The SPIR-V reader mangles the operands as signed,
_Z20__spirv_UMulExtendedll, butIGC/BiFModule/Implementation/arithmetic.cldefines only the unsigned overloads,__spirv_UMulExtended(ulong Operand1, ulong Operand2)and friends, which mangle to_Z20__spirv_UMulExtendedmm. There is no signed-typed__spirv_UMulExtendedin that file. The same module withOpSMulExtendedinstead builds fine, because there the signed mangling matches__spirv_SMulExtended(long, long).Tested:
This looks fixed upstream by KhronosGroup/SPIRV-LLVM-Translator#3963 (merged 2026-08-18), which adds
OpUMulExtendedto the opcodes whose builtin arguments are mangled unsigned. That commit is onmainonly; it is not onllvm_release_170, which is whatexternal/llvm/llvm_preferred_version.cmakepoints IGC at today, so no released IGC picks it up. Would you take a backport of that into the translator release branch IGC builds against, or alternatively add the signed overloads to the BiF module?Reproducer, as SPIR-V assembly for
spirv-as:The same SPIR-V comes out of the in-tree LLVM SPIR-V backend for this input, so any language frontend that emits
llvm.umul.with.overflowhits it:Impact for us: clang emits
llvm.umul.with.overflow.i64for a C++new T[n]in device code, so any HIP or SYCL kernel that news an array fails to build. In chipStar all device code of a binary lands in one module, so the one unresolved symbol fails every kernel in the program.Update: this is about to matter on a second path as well. KhronosGroup/SPIRV-LLVM-Translator#3977 changes
llvm-spirvto lowerllvm.umul.with.overflowtoOpUMulExtendeddirectly, instead of thespirv.llvm_umul_with_overflow_*helper function it emits today. That helper is the only reason SPIR-V produced by the translator still builds on a released IGC. If #3977 lands before IGC picks up #3963, modules coming from the translator will carryOpUMulExtendedtoo, and hit the same undefined reference.