[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