Repository navigation
Conversation
compile_and_link_program rejected any program whose device reported available == CL_FALSE. With POCL_OFFLINE_COMPILE a device only builds programs and is deliberately unavailable (e.g. almaif sets Available to CL_FALSE in offline mode), so clBuildProgram failed with CL_DEVICE_NOT_AVAILABLE before the build started. Skip the check in offline mode, matching the existing handling in devices.c and clCreateContext.c.
…rams pocl_driver_build_poclbinary compiles the hidden gvar initialization kernel through pocl_driver_build_gvar_init_kernel without a callback, which also allocated the storage. The first launch then found the storage allocated and never ran the initializer, so querying CL_PROGRAM_BINARIES before running a kernel left all program-scope variables uninitialized. Only allocate the storage when the initializer actually runs, and do so under the program lock, publishing the storage after initialization, so that launches racing on different threads or queues can't see a half-initialized (or doubly allocated) buffer either. The CPU drivers' free_program also freed the storage without clearing the pointer, so after clBuildProgram on an already built program the initializer was skipped and kernels used freed memory.
…d root With the device list [sub1, sub2, other], where sub1 and sub2 partition one root device, each device must run the binary given for it and every binary_status entry must be written. The same devices listed as [other, sub1, sub2] are the control. Runs with POCL_DEVICES="pthread basic" and skips where there is no partitionable device plus a second device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he caller's index The device list is reduced to distinct root devices before the binaries are read, and the loop then indexed the caller's binaries[], lengths[] and binary_status[] by the reduced position. When sub-devices of one root come before a second root device, [sub1, sub2, other] reduces to [other, root]: `other` was given the sub-devices' binary and ran it, and binary_status[2] was never written. Each root now reads the binary of the first caller entry that reduces to it, and its status is written to every caller entry that reduces to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ries The test leaked every program it built for its binaries, and the binaries themselves, which the asan CI job reports as leaks.
For each device with a compiler, builds and compiles a kernel with -cl-fp32-correctly-rounded-divide-sqrt. A device whose CL_DEVICE_SINGLE_FP_CONFIG has CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT must return CL_SUCCESS; one without it must return CL_BUILD_PROGRAM_FAILURE from clBuildProgram and CL_COMPILE_PROGRAM_FAILURE from clCompileProgram, with the reason in the build log, so a driver that cannot compile the kernel does not pass by accident. Labelled for CUDA, Vulkan and Level Zero. The CUDA and Vulkan devices never set the bit and take the failure path; the x86-64 CPU devices set it and take the success path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The API spec says clBuildProgram and clCompileProgram fail for a device whose CL_DEVICE_SINGLE_FP_CONFIG lacks CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT when -cl-fp32-correctly-rounded-divide-sqrt is given. compile_and_link_program has had that check since f391158, but process_options set the flag it reads only inside the branch for cl_program_link_options, which does not contain this option. So the check never ran, and the option was accepted on every device, including CUDA, Vulkan and RISC-V CPUs without F, none of which set the bit. Set the flag where the option is accepted as a compile option, by exact token match, and only when compiling, so clLinkProgram accepts it as before. A build for several devices still fails as a whole when one of them lacks the bit. Level Zero devices do not set the bit, so the option is now refused there, as the spec requires: PoCL does not pass it to the device compiler, and their sqrt is not correctly rounded under it (the CTS sqrt_cr test fails by 0.66 ulp on an Intel UHD when the bit is mapped from ZE_DEVICE_FP_FLAG_ROUNDED_DIVIDE_SQRT). Mapping it waits until the build requests correct rounding. Fixes pocl#2404 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The AArch64 CPU device reported the minimum single_fp_config, without CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT, which x86-64 and RISC-V with F set. Now that -cl-fp32-correctly-rounded-divide-sqrt is checked against the bit (pocl#2404), builds with the option would start failing on ARM64. The device is correctly rounded: float division compiles to FDIV and the kernel library's sqrt (Sleef_sqrtf_u05, which is __builtin_sqrtf) to FSQRT, in scalar and vector code, and both instructions are correctly rounded. Measured through PoCL on a Neoverse N1 with LLVM 19, in a conformance and a default build: fp32 x/y on 16.8M pairs (random, subnormal and edge cases), and 1/x and sqrt on all 2^32 inputs, in scalar and float4 kernels, gave no result that was not correctly rounded. With the bit set, the CTS divide_cr and sqrt_cr tests, which are skipped without it, pass at 0 ulp for every vector size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A device that offers cl_khr_subgroups must let an application reach the extension's entry point through clGetExtensionFunctionAddressForPlatform. Check that it does, and that it answers as the core function does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orPlatform cl_khr_subgroups has one entry point, clGetKernelSubGroupInfoKHR, which an application gets by name from clGetExtensionFunctionAddressForPlatform. PoCL's list there had no entry for it, and PoCL exports no symbol of that name, so without an ICD loader (ENABLE_ICD=OFF) a device that advertises cl_khr_subgroups gave no way to reach it: OpenCL-CTS test_subgroups fails its *_ext sub-tests with "clGetKernelSubGroupInfoKHR function not available". Through a loader the name resolves (ocl-icd answers it from its own table; the Khronos loader's cl_khr_icd2 path asks clIcdGetFunctionAddressForPlatformKHR, which has had it since pocl#2337), which hid the gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The analysis caches uniformity by Value pointer and opts out of invalidation. Later transformations can delete a value and allocate another at the same address, which then inherits the cached uniformity. An incorrect "uniform" result makes WorkitemLoops skip saving the value separately for each work-item, so later uses can read the last work-item's value. Whether this happens depends on the allocator, so a kernel can compile correctly or not depending on the process's history. Store a WeakVH in each cache entry, and reject entries whose handle no longer matches the queried value.
VariableUniformityAnalysis: don't reuse the uniformity of deleted values
cl_half_to_float shifted the uint16_t sign, promoted to int, left by 31, which is undefined for a negative half and flagged by UBSan. Widen it to uint32_t first. KhronosGroup/OpenCL-Headers has the same shift, so note the local change for whoever syncs the header next.
The _Float16 half printf formatter makes GCC emit calls to __extendhfdf2 and __extendhfxf2, which x86 libgcc_s only exports from GCC 12 on. A libpocl built with GCC 12 then fails to load in a process that already loaded an older libgcc_s, e.g. under `nsys profile`. Decode the half bits with pocl_half_to_float instead. The conversion is exact, so the output is unchanged, and half printf now also works without host _Float16 support, where it printed an error and the raw bits. Extend the halfn printf test with subnormals, the largest finite half, -0, -inf and a negative NaN.
pocl_cmd_max_grid_dim_width multiplied the z local size with itself instead of with the number of groups, and ignored the global offset. A grid that is wide only in z, or that is small but offset far from the origin, therefore got the work-group function specialized for small grids, whose range assumptions on the group counts and global offsets then let the optimizer fold e.g. get_num_groups(2) > 65535 or get_global_offset(0) > 65535 to false.
Bumps the codeql-dependencies group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.39.0 to 4.39.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@6f2ac81...9b32540) Updates `github/codeql-action/analyze` from 4.39.0 to 4.39.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@6f2ac81...9b32540) Updates `github/codeql-action/upload-sarif` from 4.39.0 to 4.39.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@6f2ac81...9b32540) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.39.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-dependencies - dependency-name: github/codeql-action/analyze dependency-version: 4.39.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-dependencies - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.39.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: codeql-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
…ql-dependencies-52fc152648 build(deps): bump the codeql-dependencies group with 3 updates
Check -cl-fp32-correctly-rounded-divide-sqrt against each device
…able-device Allow building for unavailable devices in offline compile mode
Fix program-scope variable initialization on queried and rebuilt programs
find_index_for_root_device for caller_index, original_device_list and original_num_devices for the caller's list, loop counters declared in the loops, and the comment on set_binary_status as suggested. No change in behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Return clGetKernelSubGroupInfoKHR from clGetExtensionFunctionAddressForPlatform
…ices of one root A program keeps one binary per root device, so two sub-devices of one root given different binaries cannot both be kept: kernels on the second would run the first one's binary. clCreateProgramWithBinary must fail with CL_INVALID_BINARY and say so in both entries of binary_status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A program keeps one binary per root device, and sub-devices use their root's slot. When two entries of the caller's list reduce to the same root and carry different binaries, the second could not be kept and kernels on its device would run the first's. Fail with CL_INVALID_BINARY and set it in both entries of binary_status. Identical binaries, the usual case for a program built once for both sub-devices, keep working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In LLVM 24, the llvm::PassBuilder constructor was updated to take llvm::LLVMContext &Context as its first parameter: llvm/llvm-project@910cdcd (PR llvm/llvm-project#233150) Guard the PassBuilder constructor calls in pocl_llvm_wg.cc with #if LLVM_MAJOR >= 24 to pass Context when building with LLVM 24 while preserving backward compatibility with older LLVM versions.
In LLVM 24, llvm::BasicBlock::getFirstNonPHIOrDbgOrLifetime() was renamed to llvm::BasicBlock::getFirstNonPHIOrLifetime(): llvm/llvm-project@06d62fc (PR llvm/llvm-project#232604) Guard the call in SubCFGFormation.cc with #if LLVM_MAJOR >= 24 to support LLVM 24 while preserving backward compatibility with older LLVM versions.
Pass LLVMContext to PassBuilder constructor for LLVM 24
clCreateProgramWithBinary: read binaries and write binary_status by the caller's index
clMemAllocINTEL and clSVMAlloc detect a failed shadow clCreateBuffer only through errcode. That path is reachable when no device in the context supports cl_ext_buffer_device_address (clCreateBuffer then fails with CL_INVALID_DEVICE), and, once clCreateBuffer reports an overlapping device address as an error (pocl#2376), on an overlap too. Both were broken: clMemAllocINTEL returned NULL without writing errcode_ret. Go through the common ERROR exit instead. clSVMAlloc called pocl_raw_ptr_set_erase, which frees the item, and then POCL_MEM_FREE on the same item. Drop the second free.
pocl_svm_memcpy_common forwards to clEnqueueCopyBuffer, clEnqueueWriteBuffer or clEnqueueReadBuffer and then writes (*event)->command_type whenever event is non-NULL. When the inner call fails it does not create an event, so *event is whatever the caller left there, typically NULL, and clEnqueueSVMMemcpy or clEnqueueMemcpyINTEL crashes instead of returning the error (for example CL_INVALID_CONTEXT for a wait-list event from another context, or CL_INVALID_MEM_OBJECT for a pointer whose shadow buffer is missing). Only set the command type on success. Adds a check to runtime/test_svm.
abs() returns ugentype, so abs(INT_MIN) is (uint)2147483648. The int overload used __builtin_abs, which is undefined for INT_MIN and becomes llvm.abs with int_min_is_poison set, and the long overload negated the signed value with an nsw sub. LLVM then assumed that the result's top bit is clear, e.g. folding abs(x) >> 31 to 0 or converting abs(x) to float as a signed value. Vector overloads were split down to these scalar ones, and abs_diff() of operands with different signs uses abs(). Negate in the unsigned type instead, for all types at once, which LLVM turns into llvm.abs without the poison flag. Register the existing kernel/test_abs and kernel/test_abs_diff, which were never run, and check the top bit of abs() of the minimum value.
maleadt
added a commit
to maleadt/Yggdrasil
that referenced
this pull request
Nov 3, 2026
abs() of INT_MIN and LONG_MIN relied on signed overflow, so LLVM assumed the result's top bit is clear, e.g. folding abs(x) >> 31 to 0 (pocl/pocl#2422). Julia's abs reaches PoCL as SPIR-V s_abs. Patch 0048 for the next track, 0051 for the v7.2 track (pocl/pocl#2423, JuliaGPU/pocl#11).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fork copy of pocl#2423 (fixes pocl#2422), for the Julia JLLs.
abs()ofINT_MINandLONG_MINrelied on signed overflow (__builtin_absforint,-aforlong), so LLVM assumed the result's top bit is clear:abs(x) >> 31folded to 0, andconvert_float(abs(x))gave-2147483648.0. Julia'sabsreaches PoCL as SPIR-Vs_abs, so e.g.unsigned(abs(typemin(Int32)))was wrong on KernelAbstractions.jl's POCL back end, as was Julia 1.13's integer toBFloat16conversion oftypemin, which usesabs.abs()now negates in the unsigned result type.kernel/test_absandkernel/test_abs_diffare registered with CTest, andtest_abschecks the top bit ofabs()of the minimum value.The 7.2 backport, as shipped in the Julia JLLs, is on
tb/integer-abs-7.2(on top oftb/spirv-bfloat16-7.2).Tested with LLVM 22.1.8: the new check fails without the fix and passes with it on both branches; ctest passes on main (349/349) and 7.2 (358/358). With the 7.2 build,
unsigned(abs(x))fortypeminofInt8toInt64(stored, shifted, converted toFloat32) is correct through KernelAbstractions.jl on Julia 1.12 and 1.13, and so isBFloat16(typemin(Int32))/BFloat16(typemin(Int64))on 1.13 with JuliaGPU/GPUCompiler.jl#1076.