[llvm] 6844989 - [GlobalISel] emit G_BITCAST in widenScalarMergeValues when DstTy does not match WideTy (#203014)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 3 05:16:22 PDT 2026
Author: Kacper Doga
Date: 2026-08-03T14:16:17+02:00
New Revision: 68449895e267174801d3b11afd957548f5796a41
URL: https://github.com/llvm/llvm-project/commit/68449895e267174801d3b11afd957548f5796a41
DIFF: https://github.com/llvm/llvm-project/commit/68449895e267174801d3b11afd957548f5796a41.diff
LOG: [GlobalISel] emit G_BITCAST in widenScalarMergeValues when DstTy does not match WideTy (#203014)
**Problem:**
`LegalizerHelper::widenScalarMergeValues` does not handle the case where
the destination register type is a floating-point type but the widen
operation produces an integer type of the same size.
For example, given:
```
%0:_(i8) = G_CONSTANT i8 0
%1:_(i8) = G_CONSTANT i8 1
%2:_(f16) = G_MERGE_VALUES %0:_(i8), %1_:(i8)
```
With a `minScalarOrElt` rule widening the source type to I16,
`widenScalarMergeValues` enters the `WideSize >= DstSize` path and
assigns the result to a new virtual register. The condition used to
assign directly to DstReg uses type equality (WideTy == DstTy), which
fails when DstTy = f16. As a result, DstReg is left without a
definition.
**Fix:**
After the OR-reduction loop, add a check for the case where DstTy and
WideTy have equal sizes but different types, and emit a G_BITCAST.
**Testing:**
No currently upstream target exercises `G_MERGE_VALUES` with a
floating-point destination type through this widen path, as most targets
promote f16 to s16 before legalization. The fix is therefore covered by
a unit test in `LegalizerHelperTest.cpp`, modeled after the existing
`WidenScalarMergeValuesPointer` test, which directly invokes widenScalar
on a manually constructed `f16 = G_MERGE_VALUES i8, i8` instruction and
verifies that a `G_BITCAST` is emitted as the final instruction.
Added:
Modified:
llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
llvm/test/CodeGen/AMDGPU/GlobalISel/fp-int-conversions.ll
llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-merge-values.mir
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index c37ff39e8499e..65d71a481dbdc 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -2283,6 +2283,8 @@ LegalizerHelper::widenScalarMergeValues(MachineInstr &MI, unsigned TypeIdx,
MIRBuilder.buildTrunc(DstReg, ResultReg);
else if (DstTy.isPointer())
MIRBuilder.buildIntToPtr(DstReg, ResultReg);
+ else if (DstTy != WideTy)
+ MIRBuilder.buildBitcast(DstReg, ResultReg);
MI.eraseFromParent();
return Legalized;
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-int-conversions.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-int-conversions.ll
index 49b4295c86b8a..cdc83b0aab25e 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-int-conversions.ll
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/fp-int-conversions.ll
@@ -733,5 +733,55 @@ define amdgpu_ps float @fptosi_v4f32_to_v4i8_bitcast_f32(<4 x float> %v) {
%res = bitcast <4 x i8> %cvt to float
ret float %res
}
+
+define amdgpu_ps half @fptosi_v2f32_to_v2i8_bitcast_f16(<2 x float> %v) {
+; TRUE16-LABEL: fptosi_v2f32_to_v2i8_bitcast_f16:
+; TRUE16: ; %bb.0:
+; TRUE16: v_cvt_i32_f32_e32 v1, v1
+; TRUE16: v_cvt_i32_f32_e32 v2, v0
+; TRUE16: v_and_b16 v0.l, 0xff, v1.l
+; TRUE16: v_and_b16 v0.h, 0xff, v2.l
+; TRUE16: v_lshlrev_b16 v0.l, 8, v0.l
+; TRUE16: v_or_b16 v0.l, v0.h, v0.l
+; TRUE16: ; return to shader part epilog
+;
+; GFX12-LABEL: fptosi_v2f32_to_v2i8_bitcast_f16:
+; GFX12: ; %bb.0:
+; GFX12: v_cvt_i32_f32_e32 v1, v1
+; GFX12: v_cvt_i32_f32_e32 v2, v0
+; GFX12: v_and_b16 v0.l, 0xff, v1.l
+; GFX12: v_and_b16 v0.h, 0xff, v2.l
+; GFX12: v_lshlrev_b16 v0.l, 8, v0.l
+; GFX12: v_or_b16 v0.l, v0.h, v0.l
+; GFX12: ; return to shader part epilog
+ %cvt = fptosi <2 x float> %v to <2 x i8>
+ %res = bitcast <2 x i8> %cvt to half
+ ret half %res
+}
+
+define amdgpu_ps bfloat @fptosi_v2f32_to_v2i8_bitcast_bf16(<2 x float> %v) {
+; TRUE16-LABEL: fptosi_v2f32_to_v2i8_bitcast_bf16:
+; TRUE16: ; %bb.0:
+; TRUE16: v_cvt_i32_f32_e32 v1, v1
+; TRUE16: v_cvt_i32_f32_e32 v2, v0
+; TRUE16: v_and_b16 v0.l, 0xff, v1.l
+; TRUE16: v_and_b16 v0.h, 0xff, v2.l
+; TRUE16: v_lshlrev_b16 v0.l, 8, v0.l
+; TRUE16: v_or_b16 v0.l, v0.h, v0.l
+; TRUE16: ; return to shader part epilog
+;
+; GFX12-LABEL: fptosi_v2f32_to_v2i8_bitcast_bf16:
+; GFX12: ; %bb.0:
+; GFX12: v_cvt_i32_f32_e32 v1, v1
+; GFX12: v_cvt_i32_f32_e32 v2, v0
+; GFX12: v_and_b16 v0.l, 0xff, v1.l
+; GFX12: v_and_b16 v0.h, 0xff, v2.l
+; GFX12: v_lshlrev_b16 v0.l, 8, v0.l
+; GFX12: v_or_b16 v0.l, v0.h, v0.l
+; GFX12: ; return to shader part epilog
+ %cvt = fptosi <2 x float> %v to <2 x i8>
+ %res = bitcast <2 x i8> %cvt to bfloat
+ ret bfloat %res
+}
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; FAKE16: {{.*}}
diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-merge-values.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-merge-values.mir
index 84117ac429497..2a23156621fdf 100644
--- a/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-merge-values.mir
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/legalize-merge-values.mir
@@ -105,6 +105,51 @@ body: |
$vgpr0 = COPY %3
...
+---
+name: test_merge_f16_i8_i8
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_merge_f16_i8_i8
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i32) = G_SHL [[C]], [[C2]](i32)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[SHL]](i32)
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i16) = G_OR [[C1]], [[TRUNC]]
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i32) = G_ANYEXT [[OR]](i16)
+ ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(f16) = G_TRUNC [[ANYEXT]](i32)
+ ; CHECK-NEXT: [[FPEXT:%[0-9]+]]:_(f32) = G_FPEXT [[TRUNC1]](f16)
+ ; CHECK-NEXT: $vgpr0 = COPY [[FPEXT]](f32)
+ %0:_(i8) = G_CONSTANT i8 0
+ %1:_(i8) = G_CONSTANT i8 1
+ %2:_(f16) = G_MERGE_VALUES %0(i8), %1(i8)
+ %3:_(f32) = G_FPEXT %2(f16)
+ $vgpr0 = COPY %3(f32)
+...
+
+---
+name: test_merge_bf16_i8_i8
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_merge_bf16_i8_i8
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i32) = G_SHL [[C]], [[C2]](i32)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[SHL]](i32)
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i16) = G_OR [[C1]], [[TRUNC]]
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i32) = G_ANYEXT [[OR]](i16)
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i32) = G_SHL [[ANYEXT]], [[C3]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f32) = G_BITCAST [[SHL1]](i32)
+ ; CHECK-NEXT: $vgpr0 = COPY [[BITCAST]](f32)
+ %0:_(i8) = G_CONSTANT i8 0
+ %1:_(i8) = G_CONSTANT i8 1
+ %2:_(bf16) = G_MERGE_VALUES %0(i8), %1(i8)
+ %3:_(f32) = G_FPEXT %2(bf16)
+ $vgpr0 = COPY %3(f32)
+...
+
---
name: test_merge_s24_s8_s8_s8
body: |
More information about the llvm-commits
mailing list