This is an automated email from the ASF dual-hosted git repository.
tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 93a4a644b0 [Test] Run round ties-to-even test on every backend that
implements it (#20252)
93a4a644b0 is described below
commit 93a4a644b02a9a37a3324dc3006a3d011240dc16
Author: LngelKyo <[email protected]>
AuthorDate: Wed Sep 2 02:02:10 2026 +0900
[Test] Run round ties-to-even test on every backend that implements it
(#20252)
#19368 aligned `tir.round` to ties-to-even across LLVM, CUDA, NVPTX,
ROCm, Hexagon, Metal, OpenCL, SPIR-V and WebGPU, and #20131 fixed the C
host — but the shape of that bug can recur: `test_round_ties_to_even`
(tests/python/tirx-base/test_tir_intrin.py) is pinned to
`target="llvm"`, so the C host's ties behaviour is not exercised
anywhere. The C host passed every test for four months for exactly this
reason.
**What this PR does**
Parametrize `test_round_ties_to_even` over the two host targets the
regression can recur on — `c` and `llvm` — following the
`test_codegen_loop_step` convention. Device backends have their own
lowering rules and belong in their own codegen tests. The midpoint
vector is the one #19368 added, reused verbatim.
**Verification.** On a local build: reverting the C rule to its
pre-#20131 ties-away-from-zero form makes `test_round_ties_to_even[c]`
fail with the expected midpoint mismatches (`0.5 → 1.0 vs 0.0`, `2.5 →
3.0 vs 2.0`, `−0.5 → −1.0 vs −0.0`), while `[llvm]` still passes — i.e.
the test now catches the class of bug that #20131 fixed. With the rule
restored, both targets pass. Formatting checked with ruff format/ruff
check — both the CI-pinned ruff 0.12.3 and 0.15.14 pass clean on the
touched file.
**Second commit** is docs-only: it pins the floating-point-environment
assumption from the #20131 discussion into `src/target/intrin_rule.cc`.
nearbyint is ties-to-even under the default `FE_TONEAREST`, and the same
holds for the LLVM rule, which lowers to `llvm.nearbyint` —
rounding-mode sensitive, like nearbyint itself (`llvm.roundeven` is the
mode-independent one). Every other backend that registers `tirx.round` —
cuda, nvptx, rocm, hexagon, metal, opencl, vulkan, webgpu — emits code
for a separate device whose rounding mode is fixed at RNE, so
`fesetround()` in the host process cannot reach it.
**Third commit** is a one-line CI restore: #20131 left a 3-line wrapped
call in `test_target_codegen_c_host.py` that ruff-format rejoins under
`line-length = 100`, so `pre-commit run --all-files` fails on every open
PR. This PR carries the rejoin so that merging it leaves main
lint-green. Reproduce: `uvx [email protected] format --check
tests/python/codegen/test_target_codegen_c_host.py`.
---------
Co-authored-by: LngelKyo <[email protected]>
---
src/target/intrin_rule.cc | 9 +++++++++
tests/python/tirx-base/test_tir_intrin.py | 14 +++++++++++---
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/src/target/intrin_rule.cc b/src/target/intrin_rule.cc
index 9d3a26cdd1..22d2d97d42 100644
--- a/src/target/intrin_rule.cc
+++ b/src/target/intrin_rule.cc
@@ -38,6 +38,15 @@ using tirx::FLowerIntrinsic;
// is ties-AWAY-from-zero, so lowering through FloatSuffix would disagree with
// the folder and with every other backend. Rename to nearbyint before the
// float suffix is applied, as the CUDA rule already does.
+//
+// Like nearbyint in general, this honours the current floating-point
+// environment: it is ties-to-even under the default FE_TONEAREST. The only
+// other backend a host fesetround() can reach is llvm, which lowers to
+// llvm.nearbyint (also mode-sensitive; llvm.roundeven is the
+// mode-independent one). Every other backend that registers tirx.round --
+// cuda, nvptx, rocm, hexagon, metal, opencl, vulkan, webgpu -- emits code
+// for a separate device whose rounding mode is fixed at RNE, so the
+// host's mode cannot reach it, whichever intrinsic the rule names.
struct FloatSuffixTiesToEven {
std::string operator()(const PrimType& ty, std::string name) const {
if (name == "round") name = "nearbyint";
diff --git a/tests/python/tirx-base/test_tir_intrin.py
b/tests/python/tirx-base/test_tir_intrin.py
index 6b31133b13..561e3d6761 100644
--- a/tests/python/tirx-base/test_tir_intrin.py
+++ b/tests/python/tirx-base/test_tir_intrin.py
@@ -60,15 +60,23 @@ def test_nearbyint():
tvm.testing.assert_allclose(a_rounded.numpy(), np.rint(a.numpy()))
-def test_round_ties_to_even():
- """Test that tir.round uses ties-to-even (banker's rounding) semantics."""
+# Covers the host targets the regression can recur on: the C host lowers
+# through src/target/intrin_rule.cc (the path #19368 left behind and #20131
+# fixed), llvm through llvm.nearbyint. Device backends have their own
+# lowering rules and belong in their own codegen tests.
[email protected]("target", ["c", "llvm"])
+def test_round_ties_to_even(target):
+ """Test that tirx.round uses ties-to-even (banker's rounding) semantics."""
+ if target != "c" and not tvm.testing.device_enabled(target):
+ pytest.skip(f"{target} not enabled")
+
m = te.var("m")
A = te.placeholder((m,), name="A")
A_rounded = te.compute((m,), lambda *i: tvm.tirx.round(A(*i)), name="A")
mod = te.create_prim_func([A, A_rounded])
sch = tvm.s_tir.Schedule(mod)
- func = tvm.compile(sch.mod, target="llvm")
+ func = tvm.compile(sch.mod, target=target)
dev = tvm.cpu(0)
# Midpoint values where ties-to-even and ties-away differ