@@ -1994,10 +1994,17 @@ void TaskCodeGenLLVM::finalize_offloaded_task_function() {
19941994 current_task->ad_stack .allocas = ad_stack_allocas_info_;
19951995 current_task->ad_stack .size_exprs = ad_stack_size_exprs_;
19961996 current_task->ad_stack .bound_expr = ad_stack_static_bound_expr_;
1997- // recognize `MaxOverRange` nodes that the runtime can reduce in parallel via the dedicated max-reducer dispatch
1997+ // Recognize `MaxOverRange` nodes that the runtime can reduce in parallel via the dedicated max-reducer dispatch
19981998 // instead of letting the per-thread sizer enumerate. Indexing matches `ad_stack_size_exprs_` (same iteration order
1999- // as the pre-scan above).
2000- current_task->ad_stack .max_reducer_specs = recognize_adstack_max_reducer_specs (ad_stack_size_exprs_);
1999+ // as the pre-scan above). Gated on the same `ad_stack_sparse_threshold_bytes` predicate that decides whether
2000+ // `analyze_adstack_static_bounds` captures `bound_expr`: when the dense worst-case adstack footprint fits under the
2001+ // threshold, the per-thread sizer's host-eval path resolves the size_exprs cheaply and the parallel dispatch's
2002+ // per-launch h2d copy + reducer kernel + d2h readback is strictly more expensive than the work it replaces. Above
2003+ // threshold, the kernel is in sparse mode and a captured `MaxOverRange` whose iteration count would otherwise blow
2004+ // the `1<<24` cap is the reason the max-reducer exists. Mirrors the gate in `spirv_codegen.cpp`.
2005+ if (ad_stack_static_bound_expr_.has_value ()) {
2006+ current_task->ad_stack .max_reducer_specs = recognize_adstack_max_reducer_specs (ad_stack_size_exprs_);
2007+ }
20012008 // Snodes the task body mutates. Persisted on `OffloadedTask::snode_writes` so the LLVM
20022009 // launcher can invalidate the per-task adstack metadata cache when a kernel that runs in
20032010 // between mutated a SNode an enclosing `size_expr::FieldLoad` reads. Mirrors the SPIR-V
0 commit comments