Skip to content

[BugFix][Target][LLVM] Use libm for asin/acos instead of buggy inline Taylor#19567

Open
swjng wants to merge 2 commits into
apache:mainfrom
swjng:fix/llvm-asin-acos-precision
Open

[BugFix][Target][LLVM] Use libm for asin/acos instead of buggy inline Taylor#19567
swjng wants to merge 2 commits into
apache:mainfrom
swjng:fix/llvm-asin-acos-precision

Conversation

@swjng
Copy link
Copy Markdown
Contributor

@swjng swjng commented May 15, 2026

Summary

tirx.asin's LLVM legalize used a 6-term Taylor series for |x| < 0.5 with wrong recurrence coefficients. The ratios in the code (9/40, 25/112, 1225/3456, 3969/28160) don't match the real asin series (9/20, 25/42, 49/72, 81/110), so mid-range inputs lose ~1e-3 of precision — over 1000 float32 ULP. acos inherits it via π/2 − asin(x).

x=0.47  ORT=0.48929077  TVM(old)=0.48820966  err=-1.08e-3

The Taylor branch was added in #17945 as the initial implementation, with no libm fallback. #18582 later patched only |x| ≥ 0.5 by routing to the libm extern, leaving the buggy mid-range in place. I see no evidence the inline series was an intentional fast-path.

Fix

Drop the inline series, route the whole domain through the existing asinf/acosf extern, keep the out-of-range NaN guard. Max error over x ∈ [-1, 1] drops to 2.4e-7 (ULP-grade).

Tests

If the inline polynomial was intentional for some target/path, please flag it — I'll restore it with corrected coefficients instead.

Atan is still disabled; that's a separate x² + 1 overflow bug (#19560).

Fixes #19563.

The `tirx.asin` LLVM legalization rule used a 6-term Taylor series for
|x| < 0.5 with incorrect coefficients. The recurrence multipliers
(`9/40`, `25/112`, `1225/3456`, `3969/28160`) do not match the standard
asin Taylor series ratios (`9/20`, `25/42`, `49/72`, `81/110`), so the
series under-counted higher-order terms and lost roughly 1e-3 of
precision in the mid-range (x ≈ 0.3–0.5) — well over a thousand ULP at
float32. `tirx.acos` inherits the same error via `π/2 - asin(x)` for
|x| < 0.5.

The Taylor branch was added as the original implementation in apache#17945
with no libm fallback; apache#18582 later patched the boundary at |x| ≥ 0.5
by switching to the libm extern. There is no evidence the inline series
was ever a deliberate fast-path — it was simply incomplete.

This change drops the inline series and routes the whole input range
through the existing libm extern (`asinf`/`acosf`), keeping only the
out-of-range NaN guard. ULP-grade precision is restored across the full
domain. The Asin/Acos cases that were commented out of `test_unary`
with a "Taylor approximation precision loss" TODO are re-enabled.

Fixes apache#19563.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the Taylor series approximations for asin and acos with direct libm calls in the LLVM intrinsic rules to resolve precision issues, and enables the corresponding ONNX frontend tests. Review feedback indicates that the manual NaN guards for these operations are now redundant since the library functions handle out-of-range inputs natively, and suggests simplifying the return expressions to reduce complexity in the generated TIR.

Comment thread src/target/llvm/intrin_rule_llvm.cc Outdated
Comment thread src/target/llvm/intrin_rule_llvm.cc Outdated
Per review feedback: with the inline Taylor branch removed, the manual
out-of-range Select(out_range, NaN, lib_result) is redundant. The libm
externs (asinf / acosf) already return NaN for |x| > 1 by C11 and
IEEE 754, so the guard only adds dead TIR. Verified against ±2.0 inputs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Relax ONNX Asin/Acos lose precision for mid-range float32 inputs

1 participant