diff --git a/Project.toml b/Project.toml index 55486501..88b57ba4 100644 --- a/Project.toml +++ b/Project.toml @@ -39,7 +39,7 @@ Adapt = "4" CEnum = "0.4, 0.5" ExprTools = "0.1" GPUArrays = "11.5.14" -GPUCompiler = "2.8" +GPUCompiler = "2.9" GPUToolbox = "3.1" KernelAbstractions = "0.9.39" LLVM = "6, 7, 8, 9" diff --git a/src/compiler/compilation.jl b/src/compiler/compilation.jl index f1472403..8f463103 100644 --- a/src/compiler/compilation.jl +++ b/src/compiler/compilation.jl @@ -71,7 +71,6 @@ function GPUCompiler.finish_module!(job::oneAPICompilerJob, mod::LLVM.Module, return entry end -# finish_ir! runs later in the pipeline, after optimizations that create nested insertvalue function GPUCompiler.finish_ir!(job::oneAPICompilerJob, mod::LLVM.Module, entry::LLVM.Function) # Initialize the heap before SPIR-V lowering converts the state to a reference. @@ -81,12 +80,6 @@ function GPUCompiler.finish_ir!(job::oneAPICompilerJob, mod::LLVM.Module, Tuple{CompilerJob{SPIRVCompilerTarget}, typeof(mod), typeof(entry)}, job, mod, entry) - # FIX: Flatten nested insertvalue instructions to work around SPIR-V bug - # See: https://github.com/JuliaGPU/oneAPI.jl/issues/259 - # Intel's SPIR-V runtime has a bug where OpCompositeInsert with nested - # indices (e.g., "1 0") corrupts adjacent struct fields. - flatten_nested_insertvalue!(mod) - # When the device supports BFloat16 but the SPIR-V runtime doesn't accept # SPV_KHR_bfloat16, lower all bfloat types to i16 so the back-end/translator can # handle the module without the extension. Both conditions are read from the @@ -154,111 +147,6 @@ function uses_heap(mod::LLVM.Module, T_state::LLVMType, heap_field::Integer) return false end -# Flatten nested insertvalue instructions -# This works around a bug in Intel's SPIR-V runtime where OpCompositeInsert -# with nested array indices corrupts adjacent struct fields. -function flatten_nested_insertvalue!(mod::LLVM.Module) - changed = false - count = 0 - - for f in functions(mod) - isempty(blocks(f)) && continue - - for bb in blocks(f) - # Collect instructions to process (can't modify while iterating) - to_process = LLVM.Instruction[] - - for inst in instructions(bb) - # Check if this is an insertvalue with nested indices - if LLVM.API.LLVMGetInstructionOpcode(inst) == LLVM.API.LLVMInsertValue - num_indices = LLVM.API.LLVMGetNumIndices(inst) - if num_indices > 1 - push!(to_process, inst) - end - end - end - - # Flatten each nested insertvalue - for inst in to_process - try - flatten_insert!(inst) - changed = true - count += 1 - catch e - @warn "Failed to flatten nested insertvalue" exception=(e, catch_backtrace()) - end - end - end - end - - return changed -end - -function flatten_insert!(inst::LLVM.Instruction) - # Transform: insertvalue %base, %val, i, j, k... - # Into: extractvalue %base, i - # insertvalue %extracted, %val, j, k... - # insertvalue %base, %modified, i - - composite = LLVM.operands(inst)[1] - value = LLVM.operands(inst)[2] - - num_indices = LLVM.API.LLVMGetNumIndices(inst) - idx_ptr = LLVM.API.LLVMGetIndices(inst) - indices = unsafe_wrap(Array, idx_ptr, num_indices) - - builder = LLVM.IRBuilder() - LLVM.position!(builder, inst) - - # Strategy: Recursively extract and insert for each nesting level - # For insertvalue %base, %val, i, j, k - # Do: %tmp1 = extractvalue %base, i - # %tmp2 = extractvalue %tmp1, j - # %tmp3 = insertvalue %tmp2, %val, k - # %tmp4 = insertvalue %tmp1, %tmp3, j - # %result = insertvalue %base, %tmp4, i - - # But that's complex. Simpler approach for 2-3 levels: - # Just do one level of flattening at a time - first_idx = indices[1] - rest_indices = indices[2:end] - - # Extract the first level - extracted = LLVM.extract_value!(builder, composite, first_idx) - - # Now insert into the extracted value using remaining indices - # The LLVM IR builder will handle this correctly - inserted = extracted - if length(rest_indices) == 1 - # Simple case: just one more level - inserted = LLVM.insert_value!(builder, extracted, value, rest_indices[1]) - else - # Multiple levels: need to extract down, insert, then insert back up - # For now, recursively extract to the deepest level - temps = [extracted] - for i in 1:(length(rest_indices)-1) - temp = LLVM.extract_value!(builder, temps[end], rest_indices[i]) - push!(temps, temp) - end - - # Insert the value at the deepest level - inserted = LLVM.insert_value!(builder, temps[end], value, rest_indices[end]) - - # Insert back up the chain - for i in (length(rest_indices)-1):-1:1 - inserted = LLVM.insert_value!(builder, temps[i], inserted, rest_indices[i]) - end - end - - # Insert the modified structure back into the original - result = LLVM.insert_value!(builder, composite, inserted, first_idx) - - LLVM.replace_uses!(inst, result) - LLVM.API.LLVMInstructionEraseFromParent(inst) - LLVM.dispose(builder) -end - - # Lower bfloat types to i16 in the LLVM IR. # This is needed when the device supports BFloat16 but the SPIR-V runtime/translator # doesn't support SPV_KHR_bfloat16. Since sizeof(bfloat)==sizeof(i16)==2, the memory @@ -421,7 +309,8 @@ end extensions_str = join(map(ext -> "+$ext", extensions), ",") # create GPUCompiler objects - target = SPIRVCompilerTarget(; backend, extensions = extensions_str, supports_fp16, supports_fp64, supports_bfloat16, kwargs...) + target = SPIRVCompilerTarget(; backend, extensions = extensions_str, supports_fp16, supports_fp64, supports_bfloat16, + driver = :intel, kwargs...) params = oneAPICompilerParams() CompilerConfig(target, params; kernel, name, always_inline) end diff --git a/src/compiler/precompile.jl b/src/compiler/precompile.jl index 3d5567d8..ea6a0e48 100644 --- a/src/compiler/precompile.jl +++ b/src/compiler/precompile.jl @@ -26,7 +26,7 @@ if _precompile_lts ? SPIRV_LLVM_Translator_jll.is_available() : target = SPIRVCompilerTarget(; backend = (_precompile_lts ? :khronos : :llvm), extensions = "", supports_fp16 = true, - supports_fp64 = true, supports_bfloat16 = false, + supports_fp64 = true, supports_bfloat16 = false, driver = :intel, ) params = oneAPICompilerParams() config = CompilerConfig(target, params; kernel=true, name=nothing,