[llvm-branch-commits] [llvm] release/23.x: [WebAssembly][FastISel] Fix sext i1 to i64 with +sign-ext (#213734) (#214007) (PR #214985)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Aug 8 09:21:15 PDT 2026


https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/214985

Backport ada3f57b9f2d2b26a354af2572d4fec4ace01185

Requested by: @alexrp

>From f987d864fbe88bcf4f8342607eeac3e2536d14ff Mon Sep 17 00:00:00 2001
From: Gauarv Chaudhary <137998824+ANAMASGARD at users.noreply.github.com>
Date: Wed, 5 Aug 2026 22:49:59 +0530
Subject: [PATCH] [WebAssembly][FastISel] Fix sext i1 to i64 with +sign-ext
 (#213734) (#214007)

Fixes #213734
---

## Summary

At `-O0`, WebAssembly FastISel could miscompile programs that
sign-extend an i1 value to i64 when the `+sign-ext` target feature is
enabled. The bug was introduced in LLVM 23 by the FastISel sign-ext
optimization (#179855).

For `sext i1 to i64`, FastISel fell through its switch without emitting
any instruction and returned an undefined register. Code that uses this
pattern to adjust integer division results (such as floor division)
could then compute the wrong answer. The issue reporter saw `-1` instead
of the correct `-2`.

This patch restructures `signExtend()` so i8, i16, and i32 still use
their native WebAssembly sign-extension instructions, while i1 goes
through the existing generic path: sign-extend in i32 via shifts, then
`i64.extend_i32_s`.

## Test plan

- [x] Added `i64_extend1_s` to `signext-inreg.ll` (covers FastISel and
DAG, with and without `+sign-ext`)
- [x] Verified issue repro: `llc repro.ll -O0` now returns `-2` (was
`-1`); `--fast-isel=false` still returns `-2`
- [x] `./bin/llvm-lit -j1 ../llvm/test/CodeGen/WebAssembly`

<img width="3072" height="1920" alt="image"
src="https://github.com/user-attachments/assets/0944887b-54b8-4e8d-8291-a64b03ff3786"
/>

---------

Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
(cherry picked from commit ada3f57b9f2d2b26a354af2572d4fec4ace01185)
---
 .../WebAssembly/WebAssemblyFastISel.cpp       | 30 +++++++++----------
 .../test/CodeGen/WebAssembly/signext-inreg.ll | 25 ++++++++++++++--
 2 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
index 95f4367f76cdf..0b608261932e3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -583,26 +583,23 @@ unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V,
     Register Result = createResultReg(&WebAssembly::I64RegClass);
 
     if (Subtarget->hasSignExt()) {
-      if (From != MVT::i32) {
+      switch (From) {
+      case MVT::i8:
+      case MVT::i16: {
         BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
                 TII.get(WebAssembly::I64_EXTEND_U_I32), Result)
             .addReg(Reg);
 
         Reg = Result;
         Result = createResultReg(&WebAssembly::I64RegClass);
-      }
 
-      switch (From) {
-      case MVT::i8:
         BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
-                TII.get(WebAssembly::I64_EXTEND8_S_I64), Result)
-            .addReg(Reg);
-        return Result;
-      case MVT::i16:
-        BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
-                TII.get(WebAssembly::I64_EXTEND16_S_I64), Result)
+                TII.get(From == MVT::i8 ? WebAssembly::I64_EXTEND8_S_I64
+                                        : WebAssembly::I64_EXTEND16_S_I64),
+                Result)
             .addReg(Reg);
         return Result;
+      }
       case MVT::i32:
         BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
                 TII.get(WebAssembly::I64_EXTEND_S_I32), Result)
@@ -611,14 +608,15 @@ unsigned WebAssemblyFastISel::signExtend(unsigned Reg, const Value *V,
       default:
         break;
       }
-    } else {
-      Reg = signExtendToI32(Reg, V, From);
-
-      BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
-              TII.get(WebAssembly::I64_EXTEND_S_I32), Result)
-          .addReg(Reg);
     }
 
+    Reg = signExtendToI32(Reg, V, From);
+    if (Reg == 0)
+      return 0;
+
+    BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD,
+            TII.get(WebAssembly::I64_EXTEND_S_I32), Result)
+        .addReg(Reg);
     return Result;
   }
 
diff --git a/llvm/test/CodeGen/WebAssembly/signext-inreg.ll b/llvm/test/CodeGen/WebAssembly/signext-inreg.ll
index c56ee860082c8..7b4a00cfc9f05 100644
--- a/llvm/test/CodeGen/WebAssembly/signext-inreg.ll
+++ b/llvm/test/CodeGen/WebAssembly/signext-inreg.ll
@@ -1,6 +1,6 @@
-; RUN: llc < %s -mattr=+sign-ext -fast-isel=0 -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s -mattr=+sign-ext -fast-isel=0 -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s --check-prefixes=CHECK,SLOW
 ; RUN: llc < %s -mattr=-sign-ext -asm-verbose=false -fast-isel=0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s --check-prefix=NOSIGNEXT
-; RUN: llc < %s -mattr=+sign-ext -fast-isel=1 -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s -mattr=+sign-ext -fast-isel=1 -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s --check-prefixes=CHECK,FAST
 ; RUN: llc < %s -mattr=-sign-ext -asm-verbose=false -fast-isel=1 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s --check-prefix=NOSIGNEXT
 target triple = "wasm32-unknown-unknown"
 
@@ -63,3 +63,24 @@ define i64 @i64_extend32_s(i32 %x) {
   %a = sext i32 %x to i64
   ret i64 %a
 }
+
+; CHECK-LABEL: i64_extend1_s:
+; CHECK-NEXT: .functype i64_extend1_s (i32) -> (i64){{$}}
+; SLOW-NEXT: i64.const $push[[NUM3:[0-9]+]]=, 0{{$}}
+; SLOW-NEXT: i64.extend_i32_u $push[[NUM0:[0-9]+]]=, $0{{$}}
+; SLOW-NEXT: i64.const $push[[NUM1:[0-9]+]]=, 1{{$}}
+; SLOW-NEXT: i64.and $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM1]]{{$}}
+; SLOW-NEXT: i64.sub $push[[NUM4:[0-9]+]]=, $pop[[NUM3]], $pop[[NUM2]]{{$}}
+; SLOW-NEXT: return $pop[[NUM4]]{{$}}
+; FAST-NEXT: i32.const $push[[NUM1:[0-9]+]]=, 31{{$}}
+; FAST-NEXT: i32.shl $push[[NUM0:[0-9]+]]=, $0, $pop[[NUM1]]{{$}}
+; FAST-NEXT: i32.const $push[[NUM4:[0-9]+]]=, 31{{$}}
+; FAST-NEXT: i32.shr_s $push[[NUM2:[0-9]+]]=, $pop[[NUM0]], $pop[[NUM4]]{{$}}
+; FAST-NEXT: i64.extend_i32_s $push[[NUM3:[0-9]+]]=, $pop[[NUM2]]{{$}}
+; FAST-NEXT: return $pop[[NUM3]]{{$}}
+
+; NOSIGNEXT-LABEL: i64_extend1_s
+define i64 @i64_extend1_s(i1 %x) {
+  %a = sext i1 %x to i64
+  ret i64 %a
+}



More information about the llvm-branch-commits mailing list