[llvm] [WebAssembly] Emit typed selects for reference types (PR #214811)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 7 11:55:44 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-webassembly

Author: Gauarv Chaudhary (ANAMASGARD)

<details>
<summary>Changes</summary>

  WebAssembly reference-type `select` pseudos were emitted with the untyped `select` encoding (`0x1b`), which is invalid for reference values.

  Convert reference selects to `SELECT_T` during final register-to-stack MC lowering and derive the typed-select immediate from the result register class.  The regression tests check the emitted object bytes for `externref` and
 `funcref`, while preserving the existing register-pseudo coverage.

  Refs #<!-- -->206650

---
Full diff: https://github.com/llvm/llvm-project/pull/214811.diff


4 Files Affected:

- (modified) llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td (+3-4) 
- (modified) llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp (+24) 
- (modified) llvm/test/CodeGen/WebAssembly/ref-null-zeroinitializer.ll (+1-1) 
- (modified) llvm/test/CodeGen/WebAssembly/select-reftype.ll (+10) 


``````````diff
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
index 5b6e388995b91..1bbf6eab9eb6e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
@@ -60,10 +60,9 @@ def typelist : Operand<i32> {
 }
 
 // Typed select: pops an i32 condition and two values of each declared type,
-// then pushes the declared types back. ISel continues to use the numeric
-// SELECT_<type> instructions at 0x1b; SELECT_T exists purely at the MC layer
-// so the assembler and disassembler understand the typed form. It has no
-// register operands distinct from its stack form, hence NRI.
+// then pushes the declared types back. ISel uses the reference SELECT_<type>
+// register pseudos, which MC lowering converts to SELECT_T in stack form. It
+// has no register operands distinct from its stack form, hence NRI.
 defm SELECT_T : NRI<(outs), (ins typelist:$list), [],
                     "select\t$list", 0x1c>,
                 Requires<[HasReferenceTypes]>;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
index fbff8bba50d3c..dc654e8c6309e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp
@@ -388,6 +388,25 @@ static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
 
   // Transform to _S instruction.
   auto RegOpcode = OutMI.getOpcode();
+  std::optional<wasm::ValType> SelectType;
+  switch (RegOpcode) {
+  case WebAssembly::SELECT_FUNCREF:
+  case WebAssembly::SELECT_EXTERNREF:
+  case WebAssembly::SELECT_EXNREF: {
+    const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
+    assert(MI->getOperand(0).isReg() && "select result must be a register");
+    SelectType = WebAssembly::regClassToValType(
+        MRI.getRegClass(MI->getOperand(0).getReg())->getID());
+    assert(WebAssembly::isRefType(*SelectType) &&
+           "reference select result must have a reference type");
+    OutMI.setOpcode(WebAssembly::SELECT_T);
+    RegOpcode = WebAssembly::SELECT_T;
+    break;
+  }
+  default:
+    break;
+  }
+
   auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);
   assert(StackOpcode != -1 && "Failed to stackify instruction");
   OutMI.setOpcode(StackOpcode);
@@ -399,4 +418,9 @@ static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {
       OutMI.erase(&MO);
     }
   }
+
+  if (SelectType) {
+    OutMI.addOperand(MCOperand::createImm(1));
+    OutMI.addOperand(MCOperand::createImm(static_cast<int64_t>(*SelectType)));
+  }
 }
diff --git a/llvm/test/CodeGen/WebAssembly/ref-null-zeroinitializer.ll b/llvm/test/CodeGen/WebAssembly/ref-null-zeroinitializer.ll
index 7483a72d0b96c..bdb87d486f726 100644
--- a/llvm/test/CodeGen/WebAssembly/ref-null-zeroinitializer.ll
+++ b/llvm/test/CodeGen/WebAssembly/ref-null-zeroinitializer.ll
@@ -49,7 +49,7 @@ define %externref @select_zero_externref(i1 %c, %externref %x) {
 ; CHECK-NEXT:    local.get 0
 ; CHECK-NEXT:    i32.const 1
 ; CHECK-NEXT:    i32.and
-; CHECK-NEXT:    externref.select
+; CHECK-NEXT:    select externref
 ; CHECK-NEXT:    # fallthrough-return
   %r = select i1 %c, %externref %x, %externref zeroinitializer
   ret %externref %r
diff --git a/llvm/test/CodeGen/WebAssembly/select-reftype.ll b/llvm/test/CodeGen/WebAssembly/select-reftype.ll
index 985796048fb47..b11952347a7f6 100644
--- a/llvm/test/CodeGen/WebAssembly/select-reftype.ll
+++ b/llvm/test/CodeGen/WebAssembly/select-reftype.ll
@@ -1,11 +1,19 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mattr=+reference-types -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mattr=+reference-types -O0 -filetype=obj -o %t.o
+; RUN: llvm-objdump --triple=wasm32-unknown-unknown -d %t.o | FileCheck %s --check-prefix=OBJ
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -mattr=+reference-types -O0 -fast-isel -fast-isel-abort=1 -filetype=obj -o %t.fast.o
+; RUN: llvm-objdump --triple=wasm32-unknown-unknown -d %t.fast.o | FileCheck %s --check-prefix=FAST
 
 ; Test that wasm select instruction works with reference types.
 
 target triple = "wasm32-unknown-unknown"
 
 define target("wasm.externref") @select_externref_eq(i32 %a, target("wasm.externref") %b, target("wasm.externref") %c) {
+; OBJ-LABEL: <select_externref_eq>:
+; OBJ:       1c 01 6f      select externref
+; FAST-LABEL: <select_externref_eq>:
+; FAST:       1c 01 6f      select externref
 ; CHECK-LABEL: select_externref_eq:
 ; CHECK:         .functype select_externref_eq (i32, externref, externref) -> (externref)
 ; CHECK-NEXT:  # %bb.0:
@@ -28,6 +36,8 @@ define target("wasm.externref") @select_externref_ne(i32 %a, target("wasm.extern
 }
 
 define target("wasm.funcref") @select_funcref_eq(i32 %a, target("wasm.funcref") %b, target("wasm.funcref") %c) {
+; OBJ-LABEL: <select_funcref_eq>:
+; OBJ:       1c 01 70      select funcref
 ; CHECK-LABEL: select_funcref_eq:
 ; CHECK:         .functype select_funcref_eq (i32, funcref, funcref) -> (funcref)
 ; CHECK-NEXT:  # %bb.0:

``````````

</details>


https://github.com/llvm/llvm-project/pull/214811


More information about the llvm-commits mailing list