From 1c4b412df7967aaa8773eb0a3f0578054a7fa4a0 Mon Sep 17 00:00:00 2001 From: Zheming Jin Date: Sun, 19 Jul 2026 08:43:49 -0700 Subject: [PATCH] [AMDGPU][SYCL] Run global offset + attributor before AOT codegen DPC++ HIP nd_range kernel launches fail on gfx1200 with hipErrorIllegalState because the emitted kernels carry the conservative hidden AMDGPU kernel arguments (hostcall buffer, default queue, completion action, heap). HIP tolerates these on gfx942 but rejects them on gfx1200. For AMDGPU, SYCL AOT emits the device image with `clang -cc1 -flto=full -S`: the LTO prelink optimization pipeline immediately followed by codegen, with no post-link step. AMDGPUAttributor was gated to non -prelink phases, so it never ran before codegen in this flow, leaving every kernel with the default hidden arguments. In addition, the SYCL GlobalOffsetPass only ran in the legacy codegen pipeline (too late), so even when the attributor did run it saw an unresolved `__spirv_BuiltInGlobalOffset` callee and could not prove the hidden arguments away. Run GlobalOffsetPass (self-gated to SYCL device modules) followed by AMDGPUAttributor at OptimizerLast in all non-O0 phases, including LTO prelink, and also resolve global offset before the post-link attributor. This drops the kernarg segment from 288/304 to 32/44 bytes and removes the hidden arguments, matching the verified workaround (--lto-newpm-passes=globaloffset,lto). Validated end-to-end on gfx942. See intel/llvm#22606. Co-authored-by: Cursor --- .../lib/Target/AMDGPU/AMDGPUTargetMachine.cpp | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp b/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp index 527686daebcb2..9b52cb4e326f8 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp @@ -1076,12 +1076,24 @@ void AMDGPUTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PB.registerOptimizerLastEPCallback([this](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase Phase) { - if (Level != OptimizationLevel::O0) { - if (!isLTOPreLink(Phase)) { - if (EnableAMDGPUAttributor && getTargetTriple().isAMDGCN()) { - AMDGPUAttributorOptions Opts; - MPM.addPass(AMDGPUAttributorPass(*this, Opts, Phase)); - } + if (Level != OptimizationLevel::O0 && getTargetTriple().isAMDGCN()) { + // Resolve the SYCL implicit global offset builtin (the pass self-gates + // to SYCL device modules) before running the attributor, so the + // attributor does not see an unresolved `__spirv_BuiltInGlobalOffset` + // callee and can prove that the conservative hidden kernel arguments + // (hostcall, default queue, completion action, heap) are unused. + // + // This must also run in the LTO prelink phase: for AMDGPU, SYCL AOT + // emits the device image with `clang -cc1 -flto=full -S`, i.e. the LTO + // prelink optimization pipeline immediately followed by codegen with no + // post-link step. If the attributor is skipped here (as it used to be + // for prelink), it never runs before codegen and the kernels keep the + // default hidden arguments, which HIP rejects at launch with + // hipErrorIllegalState on some GPUs (e.g. gfx1200). See intel/llvm#22606. + MPM.addPass(GlobalOffsetPass()); + if (EnableAMDGPUAttributor) { + AMDGPUAttributorOptions Opts; + MPM.addPass(AMDGPUAttributorPass(*this, Opts, Phase)); } } }); @@ -1105,6 +1117,11 @@ void AMDGPUTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) { PM.addPass(AMDGPUSwLowerLDSPass(*this)); if (EnableLowerModuleLDS) PM.addPass(AMDGPULowerModuleLDSPass(*this)); + // Also resolve the SYCL implicit global offset builtin before the + // post-link AMDGPUAttributor, for flows that defer codegen to the LTO + // link (self-gated to SYCL device modules). See intel/llvm#22606. + if (getTargetTriple().isAMDGCN()) + PM.addPass(GlobalOffsetPass()); if (Level != OptimizationLevel::O0) { // We only want to run this with O2 or higher since inliner and SROA // don't run in O1.