[llvm] [GlobalISel] Only narrow IEEE half in narrowScalarFPTOI (PR #223615)

Kane Wang via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 01:23:28 PDT 2026


https://github.com/ReVe1uv updated https://github.com/llvm/llvm-project/pull/223615

>From 0b582f2c9909c259bfd30454f2c2c464ff1c9b63 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Mon, 14 Sep 2026 20:46:37 +0800
Subject: [PATCH 1/3] [GlobalISel] Only narrow IEEE half in narrowScalarFPTOI

narrowScalarFPTOI swaps a conversion for a narrower one plus an extension,
which is only valid if every finite value of the source fits in the narrow
type. The guard tests the width, so it also accepts bfloat, which has
float's exponent range.

Match LLT::float16() instead of LLT::scalar(16). Register types come from
LLT::floatingPoint(), which degrades to ANY_SCALAR without extended LLTs,
and an ANY_SCALAR LLT matches any scalar of the same size, so this is
unchanged on such targets. AArch64 and AMDGPU only narrow LLT::float16()
sources and WebAssembly does not narrow these at all, so they are
unaffected too.
---
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    |  7 +--
 .../GlobalISel/LegalizerHelperTest.cpp        | 48 +++++++++++++++++++
 llvm/unittests/CodeGen/LowLevelTypeTest.cpp   | 27 +++++++++++
 3 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index a109d984c3e25..8729f66995a8d 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -7503,9 +7503,10 @@ LegalizerHelper::narrowScalarFPTOI(MachineInstr &MI, unsigned TypeIdx,
   LLT SrcTy = MRI.getType(Src);
 
   // If all finite floats fit into the narrowed integer type, we can just swap
-  // out the result type. This is practically only useful for conversions from
-  // half to at least 16-bits, so just handle the one case.
-  if (SrcTy.getScalarType() != LLT::scalar(16) ||
+  // out the result type. Only IEEE half qualifies: bfloat is also 16 bits wide
+  // but has float's exponent range. LLT::float16() is equivalent to
+  // LLT::scalar(16) on targets without extended LLTs.
+  if (SrcTy.getScalarType() != LLT::float16() ||
       NarrowTy.getScalarSizeInBits() < (IsSigned ? 17u : 16u))
     return UnableToLegalize;
 
diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
index 1a6abc748aeec..453bf71c5bdb6 100644
--- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
@@ -4490,4 +4490,52 @@ TEST_F(AArch64GISelMITest, LowerFMinimumExtLLT) {
   LLT::setUseExtended(false);
 }
 
+// Check that narrowScalarFPTOI only narrows conversions from IEEE half:
+// bfloat is also 16 bits wide but has float's exponent range, so swapping in
+// a narrower result could produce poison. Untyped 16-bit sources still match
+// LLT::float16() through the ANY_SCALAR wildcard.
+TEST_F(AArch64GISelMITest, NarrowFPTOIExtLLT) {
+  setUp();
+  if (!TM)
+    GTEST_SKIP();
+
+  LLT::setUseExtended(true);
+
+  DefineLegalizerInfo(A, {});
+  LLT S64 = LLT::integer(64);
+  LLT S32 = LLT::integer(32);
+
+  AInfo Info(MF->getSubtarget());
+  DummyGISelObserver Observer;
+  LegalizerHelper Helper(*MF, Info, Observer, B);
+
+  // Bfloat sources must be rejected.
+  Register BFloat16 = MRI->createGenericVirtualRegister(LLT::bfloat16());
+  auto FPTOSIBF16 = B.buildInstr(TargetOpcode::G_FPTOSI, {S64}, {BFloat16});
+  EXPECT_EQ(LegalizerHelper::LegalizeResult::UnableToLegalize,
+            Helper.narrowScalar(*FPTOSIBF16, 0, S32));
+
+  // Every finite half fits in an i32, so the conversion can be narrowed.
+  Register Half = MRI->createGenericVirtualRegister(LLT::float16());
+  auto FPTOSIF16 = B.buildInstr(TargetOpcode::G_FPTOSI, {S64}, {Half});
+  EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+            Helper.narrowScalar(*FPTOSIF16, 0, S32));
+
+  // Untyped 16-bit sources (e.g. parsed from MIR) still match.
+  Register Untyped16 = MRI->createGenericVirtualRegister(LLT::scalar(16));
+  auto FPTOSIS16 = B.buildInstr(TargetOpcode::G_FPTOSI, {S64}, {Untyped16});
+  EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+            Helper.narrowScalar(*FPTOSIS16, 0, S32));
+
+  const char *CheckStr = R"(
+  CHECK-DAG: %[[CONV:.*]]:_(i32) = G_FPTOSI %[[SRC:.*]]:_(f16)
+  CHECK-DAG: %[[DST:.*]]:_(i64) = G_SEXT %[[CONV]]
+  CHECK-DAG: %{{[0-9]+}}:_(i64) = G_FPTOSI %{{[0-9]+}}:_(bf16)
+  )";
+
+  EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
+
+  LLT::setUseExtended(false);
+}
+
 } // namespace
diff --git a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
index 411d834647d70..34128111de31f 100644
--- a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
+++ b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "llvm/ADT/APFloat.h"
 #include "llvm/CodeGen/LowLevelTypeUtils.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -449,4 +450,30 @@ TEST(LowLevelTypeTest, IsScalableVector) {
   EXPECT_TRUE(LLT::scalable_vector(2, 32).isScalableVector());
   EXPECT_TRUE(LLT::scalable_vector(1, 32).isScalableVector());
 }
+
+TEST(LowLevelTypeTest, TypedFloatMatchesUntypedScalar) {
+  // Register types come from LLT::floatingPoint(), which degrades to
+  // ANY_SCALAR without extended LLTs, and an ANY_SCALAR LLT matches any scalar
+  // of the same size. Retyping a rule as LLT::float16() is therefore a no-op
+  // until the target opts in, and precise afterwards.
+  const bool SavedUseExtended = LLT::getUseExtended();
+
+  LLT::setUseExtended(false);
+  const LLT Untyped16 = LLT::scalar(16);
+  EXPECT_TRUE(Untyped16.isAnyScalar());
+  EXPECT_EQ(LLT::float16(), Untyped16);
+  EXPECT_EQ(LLT::bfloat16(), Untyped16);
+  EXPECT_EQ(LLT::integer(16), Untyped16);
+
+  LLT::setUseExtended(true);
+  EXPECT_TRUE(LLT::float16().isFloat(APFloatBase::S_IEEEhalf));
+  EXPECT_FALSE(LLT::bfloat16().isFloat(APFloatBase::S_IEEEhalf));
+  EXPECT_NE(LLT::float16(), LLT::bfloat16());
+  EXPECT_NE(LLT::float16(), LLT::integer(16));
+  EXPECT_EQ(LLT::scalar(16), LLT::float16());
+  EXPECT_EQ(LLT::scalar(16), LLT::bfloat16());
+  EXPECT_EQ(LLT::scalar(16), LLT::integer(16));
+
+  LLT::setUseExtended(SavedUseExtended);
+}
 }

>From b42769b1def9bdfd250957a59195d00e3b57216a Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Tue, 15 Sep 2026 16:14:25 +0800
Subject: [PATCH 2/3] [GlobalISel] Saves and restores the extended LLT flag

---
 llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
index 453bf71c5bdb6..827fefbc9c9b1 100644
--- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
@@ -4499,6 +4499,7 @@ TEST_F(AArch64GISelMITest, NarrowFPTOIExtLLT) {
   if (!TM)
     GTEST_SKIP();
 
+  const bool SavedUseExtended = LLT::getUseExtended();
   LLT::setUseExtended(true);
 
   DefineLegalizerInfo(A, {});
@@ -4535,7 +4536,7 @@ TEST_F(AArch64GISelMITest, NarrowFPTOIExtLLT) {
 
   EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
 
-  LLT::setUseExtended(false);
+  LLT::setUseExtended(SavedUseExtended);
 }
 
 } // namespace

>From 202032968243e691763539af6a047880cd206380 Mon Sep 17 00:00:00 2001
From: Kane Wang <wangqiang1 at kylinos.cn>
Date: Thu, 24 Sep 2026 16:19:27 +0800
Subject: [PATCH 3/3] [GlobalISel] Drop the TypedFloatMatchesUntypedScalar test
 (nfc)

---
 llvm/unittests/CodeGen/LowLevelTypeTest.cpp | 27 ---------------------
 1 file changed, 27 deletions(-)

diff --git a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
index 34128111de31f..411d834647d70 100644
--- a/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
+++ b/llvm/unittests/CodeGen/LowLevelTypeTest.cpp
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/APFloat.h"
 #include "llvm/CodeGen/LowLevelTypeUtils.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -450,30 +449,4 @@ TEST(LowLevelTypeTest, IsScalableVector) {
   EXPECT_TRUE(LLT::scalable_vector(2, 32).isScalableVector());
   EXPECT_TRUE(LLT::scalable_vector(1, 32).isScalableVector());
 }
-
-TEST(LowLevelTypeTest, TypedFloatMatchesUntypedScalar) {
-  // Register types come from LLT::floatingPoint(), which degrades to
-  // ANY_SCALAR without extended LLTs, and an ANY_SCALAR LLT matches any scalar
-  // of the same size. Retyping a rule as LLT::float16() is therefore a no-op
-  // until the target opts in, and precise afterwards.
-  const bool SavedUseExtended = LLT::getUseExtended();
-
-  LLT::setUseExtended(false);
-  const LLT Untyped16 = LLT::scalar(16);
-  EXPECT_TRUE(Untyped16.isAnyScalar());
-  EXPECT_EQ(LLT::float16(), Untyped16);
-  EXPECT_EQ(LLT::bfloat16(), Untyped16);
-  EXPECT_EQ(LLT::integer(16), Untyped16);
-
-  LLT::setUseExtended(true);
-  EXPECT_TRUE(LLT::float16().isFloat(APFloatBase::S_IEEEhalf));
-  EXPECT_FALSE(LLT::bfloat16().isFloat(APFloatBase::S_IEEEhalf));
-  EXPECT_NE(LLT::float16(), LLT::bfloat16());
-  EXPECT_NE(LLT::float16(), LLT::integer(16));
-  EXPECT_EQ(LLT::scalar(16), LLT::float16());
-  EXPECT_EQ(LLT::scalar(16), LLT::bfloat16());
-  EXPECT_EQ(LLT::scalar(16), LLT::integer(16));
-
-  LLT::setUseExtended(SavedUseExtended);
-}
 }



More information about the llvm-commits mailing list