[llvm] [GlobalISel] avoid G_TRUNC with floating-point G_MERGE_VALUES source (PR #206733)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 06:44:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-aarch64
Author: Kacper Doga (varev-dev)
<details>
<summary>Changes</summary>
With extended LLT, scalar LLT carry an integer/float kind information. The `G_TRUNC(G_MERGE_VALUES)` fold in `LegalizationArtifactCombiner::tryCombineTrunc` truncated, copied or rebuilt a merge directly from the merge's source register.
**Problem**
When those sources are floating-point this produces artifacts with float source operand - e.g. `i1 = G_TRUNC f32` or a rebuilt `iN = G_MERGE_VALUES f32, ...` which are bit level integer operations and must not take a float operand.
**Fix**
Reinterpret a floating-point merge source to an integer of the same size via `G_BITCAST` before truncating, copying or rebuilding the merge. So the emitted artifacts stay on integer operands. Non-float sources and non-extended-LLT builds are unaffected.
---
Full diff: https://github.com/llvm/llvm-project/pull/206733.diff
2 Files Affected:
- (modified) llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h (+18-7)
- (added) llvm/test/CodeGen/AArch64/GlobalISel/legalize-trunc-merge-float-src.mir (+72)
``````````diff
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
index d8d7ccc0bd7a7..4545c36b8f8cb 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
@@ -292,29 +292,40 @@ class LegalizationArtifactCombiner {
if (!DstTy.isScalar() || !MergeSrcTy.isScalar())
return false;
+ // G_TRUNC/G_MERGE_VALUES operate on the raw bit pattern - if the merge
+ // feeds us float sources, reinterpret them to integer of the same size
+ // so we never emit a G_TRUNC or G_MERGE_VALUES with a floating-point
+ // source operand.
+ const LLT WorkTy =
+ MergeSrcTy.isFloat() ? LLT::integer(MergeSrcSize) : MergeSrcTy;
+ auto AsInt = [&](Register R) {
+ if (MergeSrcTy.isFloat())
+ return Builder.buildBitcast(WorkTy, R).getReg(0);
+ return R;
+ };
+
if (DstSize < MergeSrcSize) {
// When the merge source is larger than the destination, we can just
// truncate the merge source directly
- if (isInstUnsupported({TargetOpcode::G_TRUNC, {DstTy, MergeSrcTy}}))
+ if (isInstUnsupported({TargetOpcode::G_TRUNC, {DstTy, WorkTy}}))
return false;
LLVM_DEBUG(dbgs() << "Combining G_TRUNC(G_MERGE_VALUES) to G_TRUNC: "
<< MI);
- Builder.buildTrunc(DstReg, MergeSrcReg);
+ Builder.buildTrunc(DstReg, AsInt(MergeSrcReg));
UpdatedDefs.push_back(DstReg);
} else if (DstSize == MergeSrcSize) {
// If the sizes match we can simply try to replace the register
LLVM_DEBUG(
dbgs() << "Replacing G_TRUNC(G_MERGE_VALUES) with merge input: "
<< MI);
- replaceRegOrBuildCopy(DstReg, MergeSrcReg, MRI, Builder, UpdatedDefs,
- Observer);
+ replaceRegOrBuildCopy(DstReg, AsInt(MergeSrcReg), MRI, Builder,
+ UpdatedDefs, Observer);
} else if (DstSize % MergeSrcSize == 0) {
// If the trunc size is a multiple of the merge source size we can use
// a smaller merge instead
- if (isInstUnsupported(
- {TargetOpcode::G_MERGE_VALUES, {DstTy, MergeSrcTy}}))
+ if (isInstUnsupported({TargetOpcode::G_MERGE_VALUES, {DstTy, WorkTy}}))
return false;
LLVM_DEBUG(
@@ -326,7 +337,7 @@ class LegalizationArtifactCombiner {
"trunc(merge) should require less inputs than merge");
SmallVector<Register, 8> SrcRegs(NumSrcs);
for (unsigned i = 0; i < NumSrcs; ++i)
- SrcRegs[i] = SrcMerge->getSourceReg(i);
+ SrcRegs[i] = AsInt(SrcMerge->getSourceReg(i));
Builder.buildMergeValues(DstReg, SrcRegs);
UpdatedDefs.push_back(DstReg);
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-trunc-merge-float-src.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-trunc-merge-float-src.mir
new file mode 100644
index 0000000000000..be338f86a7d0e
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-trunc-merge-float-src.mir
@@ -0,0 +1,72 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64 -run-pass=legalizer %s -o - | FileCheck %s
+
+# Regression test for LegalizationArtifactCombiner::tryCombineTrunc: a G_TRUNC
+# whose G_MERGE_VALUES source operands are floating-point must not fold to a
+# G_TRUNC (or rebuilt G_MERGE_VALUES) with a float source operand. The float
+# source is reinterpreted to an integer of the same size via G_BITCAST first.
+
+---
+# DstSize < MergeSrcSize - trunc folds trought the merge to the first source.
+name: trunc_merge_float_src_smaller
+legalized: true
+body: |
+ bb.0:
+ liveins: $s0, $s1
+ ; CHECK-LABEL: name: trunc_merge_float_src_smaller
+ ; CHECK: liveins: $s0, $s1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(f32) = COPY $s0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[COPY]](f32)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC [[BITCAST]](i32)
+ ; CHECK-NEXT: $h0 = COPY [[TRUNC]](s16)
+ %0:_(f32) = COPY $s0
+ %1:_(f32) = COPY $s1
+ %2:_(s64) = G_MERGE_VALUES %0(f32), %1(f32)
+ %3:_(s16) = G_TRUNC %2(s64)
+ $h0 = COPY %3(s16)
+...
+---
+# DstSize == MergeSrcSize - trunc becomes the (bitcast of the) first source.
+name: trunc_merge_float_src_equal
+legalized: true
+body: |
+ bb.0:
+ liveins: $s0, $s1
+ ; CHECK-LABEL: name: trunc_merge_float_src_equal
+ ; CHECK: liveins: $s0, $s1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(f32) = COPY $s0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[COPY]](f32)
+ ; CHECK-NEXT: $w0 = COPY [[BITCAST]](i32)
+ %0:_(f32) = COPY $s0
+ %1:_(f32) = COPY $s1
+ %2:_(s64) = G_MERGE_VALUES %0(f32), %1(f32)
+ %3:_(s32) = G_TRUNC %2(s64)
+ $w0 = COPY %3(s32)
+...
+---
+# DstSize % MergeSrcSize == 0 - rebuild a smaller merge, whose sources must be
+# the integer-reinterpreted halves, not the floats.
+name: trunc_merge_float_src_multiple
+legalized: true
+body: |
+ bb.0:
+ liveins: $h0, $h1, $h2, $h3
+ ; CHECK-LABEL: name: trunc_merge_float_src_multiple
+ ; CHECK: liveins: $h0, $h1, $h2, $h3
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(f16) = COPY $h0
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(f16) = COPY $h1
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i16) = G_BITCAST [[COPY]](f16)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(i16) = G_BITCAST [[COPY1]](f16)
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s32) = G_MERGE_VALUES [[BITCAST]](i16), [[BITCAST1]](i16)
+ ; CHECK-NEXT: $w0 = COPY [[MV]](s32)
+ %0:_(f16) = COPY $h0
+ %1:_(f16) = COPY $h1
+ %2:_(f16) = COPY $h2
+ %3:_(f16) = COPY $h3
+ %4:_(s64) = G_MERGE_VALUES %0(f16), %1(f16), %2(f16), %3(f16)
+ %5:_(s32) = G_TRUNC %4(s64)
+ $w0 = COPY %5(s32)
+...
``````````
</details>
https://github.com/llvm/llvm-project/pull/206733
More information about the llvm-commits
mailing list