[llvm] 97fd08d - [GlobalISel] avoid G_TRUNC with floating-point G_MERGE_VALUES source (#206733)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 05:24:22 PDT 2026


Author: Kacper Doga
Date: 2026-08-03T14:24:17+02:00
New Revision: 97fd08dbaf82b35ef860bca11bb047fd60f723d5

URL: https://github.com/llvm/llvm-project/commit/97fd08dbaf82b35ef860bca11bb047fd60f723d5
DIFF: https://github.com/llvm/llvm-project/commit/97fd08dbaf82b35ef860bca11bb047fd60f723d5.diff

LOG: [GlobalISel] avoid G_TRUNC with floating-point G_MERGE_VALUES source (#206733)

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.

Added: 
    llvm/test/CodeGen/AArch64/GlobalISel/legalize-trunc-merge-float-src.mir

Modified: 
    llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
index d8d7ccc0bd7a7..4a8f5297b66ef 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 as integers 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 != WorkTy)
+          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..e440a515f1b2b
--- /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 through 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]+]]:_(i16) = G_TRUNC [[BITCAST]](i32)
+    ; CHECK-NEXT: $h0 = COPY [[TRUNC]](i16)
+    %0:_(f32) = COPY $s0
+    %1:_(f32) = COPY $s1
+    %2:_(i64) = G_MERGE_VALUES %0(f32), %1(f32)
+    %3:_(i16) = G_TRUNC %2(i64)
+    $h0 = COPY %3(i16)
+...
+---
+# 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:_(i64) = G_MERGE_VALUES %0(f32), %1(f32)
+    %3:_(i32) = G_TRUNC %2(i64)
+    $w0 = COPY %3(i32)
+...
+---
+# 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]+]]:_(i32) = G_MERGE_VALUES [[BITCAST]](i16), [[BITCAST1]](i16)
+    ; CHECK-NEXT: $w0 = COPY [[MV]](i32)
+    %0:_(f16) = COPY $h0
+    %1:_(f16) = COPY $h1
+    %2:_(f16) = COPY $h2
+    %3:_(f16) = COPY $h3
+    %4:_(i64) = G_MERGE_VALUES %0(f16), %1(f16), %2(f16), %3(f16)
+    %5:_(i32) = G_TRUNC %4(i64)
+    $w0 = COPY %5(i32)
+...


        


More information about the llvm-commits mailing list