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

Derek Schuff via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 11:14:28 PDT 2026


dschuff wrote:

I think we want a bit more comprehensive fix for this problem. When the existing typed select support was added, it was really only for disassembly purposes, and if we want to generate it, I think we need to actually model it better in the backend.
With this approach `SELECT_#rc_S` still exists in TableGen with the wrong opcode 0x1b: Because `multiclass I` stamps out both a register instruction (SELECT_EXTERNREF) and a stack instruction (SELECT_EXTERNREF_S), the stack instructions SELECT_FUNCREF_S, SELECT_EXTERNREF_S, and SELECT_EXNREF_S remain registered with the assembler and disassembler with the opcode and mnemonics for the untyped select. This means that `llc` will assemble externref.select and friends with the 0x1b opcode. Also the assembly printing with `--wasm-keep-registers` is now inconsistent because the fixup is stuck in the register removal process (this in itself also seems weird actually).

Here's what Gemini is suggesting as an alternative, and it looks right to me:
#### Step 1: Clean up TableGen (`WebAssemblyInstrRef.td`)
- **Do not generate stack instructions (`_S`) with opcode `0x1b` for reference types.** Opcode `0x1b` is simply invalid for reference types in WebAssembly.
- Define `SELECT_FUNCREF`, `SELECT_EXTERNREF`, and `SELECT_EXNREF` as **register-only** pseudo/CodeGen instructions (`isCodeGenOnly = 1`), without using `multiclass I` to create `_S` variants at `0x1b`:
  ```tablegen
  multiclass REF_SELECT<WebAssemblyRegClass rc, ValueType vt> {
    let isCodeGenOnly = 1 in
    def SELECT_#rc : NI<(outs rc:$dst), (ins rc:$lhs, rc:$rhs, I32:$cond),
                        [(set rc:$dst, (select I32:$cond, rc:$lhs, rc:$rhs))],
                        /*asmstr=*/"select\t"#vt#"\t$dst, $lhs, $rhs, $cond">,
                     Requires<[HasReferenceTypes]>;
  }
  ```
  *(Note: Notice the register-form asm string uses `select <type>`, matching the standard syntax rather than `vt#".select"`).*

#### Step 2: Formalize Lowering to `SELECT_T_S`
Instead of having `SELECT_#rc_S` exist as fake instructions, let TableGen or `WebAssemblyMCInstLower` formally map the register-based `SELECT_<type>` to `SELECT_T_S`:
- In `WebAssemblyMCInstLower.cpp`:
  When lowering `SELECT_FUNCREF`, `SELECT_EXTERNREF`, `SELECT_EXNREF` (or any typed select), convert them to `SELECT_T_S` and add the `OPERAND_VALTYPE_LIST` operands.
- If `-wasm-keep-registers` is set, `SELECT_<type>` prints:
  `select externref $push0=, $2, $1, $0`
  which matches the standard mnemonic and avoids the non-existent `externref.select` instruction.

#### Step 3: Clean up MC / Assembler & Tests
- **In `reference-types.s`**: Update lines 67–86 to test the actual valid Wasm encoding:
  `select funcref # encoding: [0x1c, 0x01, 0x70]`
  `select externref # encoding: [0x1c, 0x01, 0x6f]`
  `select exnref # encoding: [0x1c, 0x01, 0x69]`
- **In `WebAssemblyAsmParser.cpp`**:
  If we want backwards compatibility for existing assembly files or tools that write `funcref.select` / `externref.select`:
  Parse `funcref.select`, `externref.select`, and `exnref.select` as aliases that emit `SELECT_T` with the corresponding `ValType`, rather than emitting `0x1b`.

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


More information about the llvm-commits mailing list