From 7d852b8dc484e5c49c6c262767f63c46d4ec9f01 Mon Sep 17 00:00:00 2001 From: zjli2013 Date: Thu, 9 Jul 2026 19:45:16 +0800 Subject: [PATCH] [AMDGPU] Keep fdiv correctly rounded under fast_math (clear afn) Under fast_math the global IRBuilder fast-math flags include afn (ApproxFunc). The AMDGPU backend uses that to lower fdiv to an approximate v_rcp_f32 reciprocal (~2.5 ULP), so exact divisions such as 180.0/180.0 return 0.99999994. This breaks floor(a/b) and the a - floor(a/b)*b modulo expansion, surfacing as the "AMDGPU modulo bug" where float % (runtime int) returns the unreduced dividend. Clear afn on the emitted FDiv so division stays correctly rounded (matching CPU/CUDA, where it is a no-op). afn is intended for transcendentals, not basic arithmetic. Fixes: quadrants#749 Co-authored-by: Cursor --- quadrants/codegen/llvm/codegen_llvm.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/quadrants/codegen/llvm/codegen_llvm.cpp b/quadrants/codegen/llvm/codegen_llvm.cpp index d085c5c6b3..be439d998d 100644 --- a/quadrants/codegen/llvm/codegen_llvm.cpp +++ b/quadrants/codegen/llvm/codegen_llvm.cpp @@ -572,6 +572,16 @@ void TaskCodeGenLLVM::visit(BinaryOpStmt *stmt) { } } else if (op == BinaryOpType::div) { if (is_real(stmt->ret_type.get_element_type())) { + // Under fast_math the builder carries `afn` (ApproxFunc). On AMDGPU that + // makes fdiv lower to an approximate v_rcp_f32 (~2.5 ULP), so exact cases + // like 180.0/180.0 return 0.99999994 and silently break floor(a/b) and the + // `a - floor(a/b)*b` modulo expansion (quadrants#749). Emit the division + // with afn cleared so it stays correctly rounded; `afn` is meant for + // transcendentals, not basic arithmetic. No-op on CPU/CUDA. + llvm::IRBuilderBase::FastMathFlagGuard fmf_guard(*builder); + auto fmf = builder->getFastMathFlags(); + fmf.setApproxFunc(false); + builder->setFastMathFlags(fmf); llvm_val[stmt] = builder->CreateFDiv(llvm_val[stmt->lhs], llvm_val[stmt->rhs]); } else if (is_signed(stmt->ret_type.get_element_type())) { llvm_val[stmt] = builder->CreateSDiv(llvm_val[stmt->lhs], llvm_val[stmt->rhs]);