Skip to content

[AMDGPU] Avoid redundant ftrunc before i64 FP-to-int conversion - #240082

Draft
lialan wants to merge 1 commit into
llvm:mainfrom
lialan:users/lialan/amdgpu_f32_i64_trunc_elision
Draft

lialan wants to merge 1 commit into
llvm:mainfrom
lialan:users/lialan/amdgpu_f32_i64_trunc_elision

Conversation

@lialan

@lialan lialan commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

LowerFP_TO_INT64 and legalizeFPTOI split a float into 32-bit halves:

tf = trunc(val)
hif = floor(tf * 2^-32)
lof = tf - hif * 2^32

This only matters when val is a negative non-integer.
For nonnegative val, floor(val * 2^-32) == floor(trunc(val) * 2^-32), and the fraction left in lof is dropped by the fptoui of the low half.

Instead we could do: use abs(val) in place of trunc(val) for signed non-integer or unsigned.

Both GlobalISel and SelectionDAG are modified.

Relevant PRs:
#239233 and #231060

LowerFP_TO_INT64 and legalizeFPTOI split a float into 32-bit halves with
hif = floor(tf * 2^-32) and lof = tf - hif * 2^32, where tf is trunc(val).
The truncation only matters when val can be a negative non-integer,
because the split floors. For a nonnegative val, floor(val * 2^-32)
already equals floor(trunc(val) * 2^-32), and the fraction left in lof is
dropped by the fptoui of the low half.

So use |val| instead of trunc(val) when:
- the input is signed f32, which already converts the magnitude and
  restores the sign afterwards;
- the conversion is unsigned, where inputs at or below -1 are poison and
  inputs in (-1, 0) convert to 0, the same as their absolute value.

Signed f64 splits val itself, so it still truncates unless val is
already an integer (the result of a rounding operation or an int-to-fp
conversion) or cannot be ordered less than zero.

The fabs folds into a source modifier. On targets without v_trunc_f64,
skipping the truncation also avoids its expansion, which happens during
legalization before the generic ftrunc combine can fold it. The change
is applied to both SelectionDAG and GlobalISel.

llvm#231060 folded fp_to_int(ftrunc(x)) generically and noted that the
AMDGPU f32-to-i64 tests did not change because this lowering emits its
own ftrunc. This patch removes that ftrunc where it is not needed.

Across the updated autogenerated tests, 141 function/target pairs get
shorter and 18 get longer. For example, fp_to_sint.ll on gfx1100 goes
from 22/34/57 to 21/29/51 instructions for scalar/v2/v4. All 18 longer
ones are uniform (SGPR) inputs on targets before gfx10, where VOP3 can
read only one scalar operand, so the folded |src| modifier needs extra
v_mov copies (at most 4 instructions, e.g. fp_to_sint_v4i64 on SI/VI).

Related open PR llvm#239233 folds floor into unsigned FP-to-int conversions
and updates several of the same tests (bf16.ll, fp_to_sint.ll,
fp_to_uint.ll, fptosi-sat-scalar.ll, fptoui-sat-*.ll, lround.ll, lrint.ll
and the GlobalISel bf16 tests); whichever lands second needs to
regenerate them. Open PR llvm#118831 moves FP/int lowering into generic
TargetLowering; this change only removes a redundant operation in the
AMDGPU expansion and does not move or share the lowering implementation.

Regenerate existing CHECKs and update the hand-written checks in
fp_to_uint.f64.ll. No test functions or IR inputs are added or changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lialan lialan changed the title G[AMDGPU] Avoid redundant ftrunc before i64 FP-to-int conversion [AMDGPU] Avoid redundant ftrunc before i64 FP-to-int conversion Nov 8, 2026

This branch has not been deployed

No deployments
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.

1 participant