Skip to content

[WebAssembly][GlobalISel] G_ADD (and in-reg ext/trunc related) legalization & selection#183694

Open
QuantumSegfault wants to merge 2 commits intollvm:mainfrom
QuantumSegfault:wasm-gisel-full-pipeline-integers
Open

[WebAssembly][GlobalISel] G_ADD (and in-reg ext/trunc related) legalization & selection#183694
QuantumSegfault wants to merge 2 commits intollvm:mainfrom
QuantumSegfault:wasm-gisel-full-pipeline-integers

Conversation

@QuantumSegfault
Copy link
Contributor

This PR enables G_ADD and immediate dependencies (relavent ext and trunc related ops) to be fully legalized and selected.

The most important change made is getting the boilerplate for RegBankSelect working.

Split from #157161.

@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2026

@llvm/pr-subscribers-llvm-globalisel

Author: Demetrius Kanios (QuantumSegfault)

Changes

This PR enables G_ADD and immediate dependencies (relavent ext and trunc related ops) to be fully legalized and selected.

The most important change made is getting the boilerplate for RegBankSelect working.

Split from #157161.


Patch is 28.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/183694.diff

8 Files Affected:

  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp (+7-3)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp (+19)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp (+89-2)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir (+140)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir (+48)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir (+200)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/regbankselect/bitwise.mir (+134)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/regbankselect/int-arithmetic.mir (+46)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index d6eee444af992..01b8b66bfe7b0 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -48,7 +48,7 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
   bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
 
   const WebAssemblyTargetMachine &TM;
-  // const WebAssemblySubtarget &STI;
+  const WebAssemblySubtarget &STI;
   const WebAssemblyInstrInfo &TII;
   const WebAssemblyRegisterInfo &TRI;
   const WebAssemblyRegisterBankInfo &RBI;
@@ -71,8 +71,8 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
 WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
     const WebAssemblyTargetMachine &TM, const WebAssemblySubtarget &STI,
     const WebAssemblyRegisterBankInfo &RBI)
-    : TM(TM), /*STI(STI),*/ TII(*STI.getInstrInfo()),
-      TRI(*STI.getRegisterInfo()), RBI(RBI),
+    : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()),
+      RBI(RBI),
 
 #define GET_GLOBALISEL_PREDICATES_INIT
 #include "WebAssemblyGenGlobalISel.inc"
@@ -84,6 +84,10 @@ WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
 }
 
 bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
+  if (!isPreISelGenericOpcode(I.getOpcode())) {
+    return true;
+  }
+
   if (selectImpl(I, *CoverageInfo))
     return true;
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 82b8a48266e31..ecbaf79db751d 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblyLegalizerInfo.h"
+#include "WebAssemblySubtarget.h"
 
 #define DEBUG_TYPE "wasm-legalinfo"
 
@@ -19,5 +20,23 @@ using namespace LegalizeActions;
 
 WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
     const WebAssemblySubtarget &ST) {
+  using namespace TargetOpcode;
+
+  const LLT s32 = LLT::scalar(32);
+  const LLT s64 = LLT::scalar(64);
+
+  getActionDefinitionsBuilder({G_CONSTANT, G_ADD, G_AND})
+      .legalFor({s32, s64})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_ASHR, G_SHL})
+      .legalFor({{s32, s32}, {s64, s64}})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64)
+      .scalarSameSizeAs(1, 0);
+
+  getActionDefinitionsBuilder(G_SEXT_INREG).lower();
+
   getLegacyLegalizerInfo().computeTables();
 }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index ff0bc298dc1da..10827acb0b16b 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -13,6 +13,7 @@
 
 #include "WebAssemblyRegisterBankInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyRegisterInfo.h"
 #include "llvm/CodeGen/TargetRegisterInfo.h"
 
 #define GET_TARGET_REGBANK_IMPL
@@ -20,7 +21,38 @@
 #include "WebAssemblyGenRegisterBank.inc"
 
 namespace llvm {
-namespace WebAssembly {} // namespace WebAssembly
+namespace WebAssembly {
+enum PartialMappingIdx {
+  PMI_None = -1,
+  PMI_I32 = 1,
+  PMI_I64,
+  PMI_Min = PMI_I32,
+};
+
+enum ValueMappingIdx {
+  InvalidIdx = 0,
+  I32Idx = 1,
+  I64Idx = 5,
+};
+
+const RegisterBankInfo::PartialMapping PartMappings[]{{0, 32, I32RegBank},
+                                                      {0, 64, I64RegBank}};
+
+const RegisterBankInfo::ValueMapping ValueMappings[] = {
+    // invalid
+    {nullptr, 0},
+    // up to 4 operands as I32
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    // up to 4 operands as I64
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+};
+} // namespace WebAssembly
 } // namespace llvm
 
 using namespace llvm;
@@ -30,5 +62,60 @@ WebAssemblyRegisterBankInfo::WebAssemblyRegisterBankInfo(
 
 const RegisterBankInfo::InstructionMapping &
 WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
-  return getInvalidInstructionMapping();
+  unsigned Opc = MI.getOpcode();
+  const MachineFunction &MF = *MI.getParent()->getParent();
+  const MachineRegisterInfo &MRI = MF.getRegInfo();
+
+  if ((Opc != TargetOpcode::COPY && !isPreISelGenericOpcode(Opc)) ||
+      Opc == TargetOpcode::G_PHI) {
+    const RegisterBankInfo::InstructionMapping &Mapping =
+        getInstrMappingImpl(MI);
+    if (Mapping.isValid())
+      return Mapping;
+  }
+
+  using namespace TargetOpcode;
+
+  const unsigned NumOperands = MI.getNumOperands();
+  const ValueMapping *OperandsMapping = nullptr;
+  unsigned MappingID = DefaultMappingID;
+
+  // Check if LLT sizes match sizes of available register banks.
+  for (const MachineOperand &Op : MI.operands()) {
+    if (Op.isReg()) {
+      LLT RegTy = MRI.getType(Op.getReg());
+
+      if (RegTy.isScalar() &&
+          (RegTy.getSizeInBits() != 32 && RegTy.getSizeInBits() != 64))
+        return getInvalidInstructionMapping();
+
+      if (RegTy.isVector() && RegTy.getSizeInBits() != 128)
+        return getInvalidInstructionMapping();
+    }
+  }
+
+  const LLT Op0Ty = MRI.getType(MI.getOperand(0).getReg());
+  unsigned Op0Size = Op0Ty.getSizeInBits();
+
+  auto &Op0IntValueMapping =
+      WebAssembly::ValueMappings[Op0Size == 64 ? WebAssembly::I64Idx
+                                               : WebAssembly::I32Idx];
+
+  switch (Opc) {
+  case G_ADD:
+  case G_AND:
+  case G_ASHR:
+  case G_SHL:
+    OperandsMapping = &Op0IntValueMapping;
+    break;
+  case G_CONSTANT:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  }
+
+  if (!OperandsMapping)
+    return getInvalidInstructionMapping();
+
+  return getInstructionMapping(MappingID, /*Cost=*/1, OperandsMapping,
+                               NumOperands);
 }
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir
new file mode 100644
index 0000000000000..c1c111606d15f
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir
@@ -0,0 +1,140 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            and_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: and_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[AND_I32_:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[AND_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %2:i32regbank(s32) = G_AND %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            and_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: and_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64 = ARGUMENT_i64 1, implicit $arguments
+    ; CHECK-NEXT: [[AND_I64_:%[0-9]+]]:i64 = AND_I64 [[ARGUMENT_i64_]], [[ARGUMENT_i64_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[AND_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64(s64) = ARGUMENT_i64 1, implicit $arguments
+    %2:i64regbank(s64) = G_AND %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
+
+---
+name:            shl_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: shl_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 5, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHL_I32_:%[0-9]+]]:i32 = SHL_I32 [[ARGUMENT_i32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHL_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32regbank(s32) = G_CONSTANT i32 5
+    %2:i32regbank(s32) = G_SHL %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            shl_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: shl_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 37, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[ARGUMENT_i64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHL_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64regbank(s64) = G_CONSTANT i64 37
+    %2:i64regbank(s64) = G_SHL %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
+
+---
+name:            ashr_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: ashr_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 5, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHR_S_I32_:%[0-9]+]]:i32 = SHR_S_I32 [[ARGUMENT_i32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHR_S_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32regbank(s32) = G_CONSTANT i32 5
+    %2:i32regbank(s32) = G_ASHR %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            ashr_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: ashr_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 37, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHR_S_I64_:%[0-9]+]]:i64 = SHR_S_I64 [[ARGUMENT_i64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHR_S_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64regbank(s64) = G_CONSTANT i64 37
+    %2:i64regbank(s64) = G_ASHR %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir
new file mode 100644
index 0000000000000..60c916c372636
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir
@@ -0,0 +1,48 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            add_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD_I32_:%[0-9]+]]:i32 = ADD_I32 [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[ADD_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %2:i32regbank(s32) = G_ADD %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            add_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64 = ARGUMENT_i64 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD_I64_:%[0-9]+]]:i64 = ADD_I64 [[ARGUMENT_i64_]], [[ARGUMENT_i64_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[ADD_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64(s64) = ARGUMENT_i64 1, implicit $arguments
+    %2:i64regbank(s64) = G_ADD %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir
new file mode 100644
index 0000000000000..0d12b749e461e
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir
@@ -0,0 +1,200 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            add_i8_zext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_zext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:i32(s32) = G_AND [[ADD]], [[C]]
+    ; CHECK-NEXT: RETURN [[AND]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_ZEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i8_sext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_sext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ADD]], [[C]](s32)
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:i32(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; CHECK-NEXT: RETURN [[ASHR]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_SEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i8_aext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_aext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32(s32) = COPY [[ADD]](s32)
+    ; CHECK-NEXT: RETURN [[COPY]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_ANYEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_zext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_zext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 65535
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:i32(s32) = G_AND [[ADD]], [[C]]
+    ; CHECK-NEXT: RETURN [[AND]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s16) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s16) = G_TRUNC %3(s32)
+    %4:_(s16) = G_ADD %0, %1
+    %5:i32(s32) = G_ZEXT %4(s16)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_sext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_sext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ADD]], [[C]](s32)
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:i32(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; CHECK-NEXT: RETURN [[ASHR]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s16) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s16) = G_TRUNC %3(s32)
+    %4:_(s16) = G_ADD %0, %1
+    %5:i32(s32) = G_SEXT %4(s16)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_aext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_aext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32(s32) = COPY [[ADD]](s32)
+    ; CHE...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2026

@llvm/pr-subscribers-backend-webassembly

Author: Demetrius Kanios (QuantumSegfault)

Changes

This PR enables G_ADD and immediate dependencies (relavent ext and trunc related ops) to be fully legalized and selected.

The most important change made is getting the boilerplate for RegBankSelect working.

Split from #157161.


Patch is 28.90 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/183694.diff

8 Files Affected:

  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp (+7-3)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp (+19)
  • (modified) llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp (+89-2)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir (+140)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir (+48)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir (+200)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/regbankselect/bitwise.mir (+134)
  • (added) llvm/test/CodeGen/WebAssembly/GlobalISel/regbankselect/int-arithmetic.mir (+46)
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index d6eee444af992..01b8b66bfe7b0 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -48,7 +48,7 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
   bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
 
   const WebAssemblyTargetMachine &TM;
-  // const WebAssemblySubtarget &STI;
+  const WebAssemblySubtarget &STI;
   const WebAssemblyInstrInfo &TII;
   const WebAssemblyRegisterInfo &TRI;
   const WebAssemblyRegisterBankInfo &RBI;
@@ -71,8 +71,8 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
 WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
     const WebAssemblyTargetMachine &TM, const WebAssemblySubtarget &STI,
     const WebAssemblyRegisterBankInfo &RBI)
-    : TM(TM), /*STI(STI),*/ TII(*STI.getInstrInfo()),
-      TRI(*STI.getRegisterInfo()), RBI(RBI),
+    : TM(TM), STI(STI), TII(*STI.getInstrInfo()), TRI(*STI.getRegisterInfo()),
+      RBI(RBI),
 
 #define GET_GLOBALISEL_PREDICATES_INIT
 #include "WebAssemblyGenGlobalISel.inc"
@@ -84,6 +84,10 @@ WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
 }
 
 bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
+  if (!isPreISelGenericOpcode(I.getOpcode())) {
+    return true;
+  }
+
   if (selectImpl(I, *CoverageInfo))
     return true;
 
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 82b8a48266e31..ecbaf79db751d 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "WebAssemblyLegalizerInfo.h"
+#include "WebAssemblySubtarget.h"
 
 #define DEBUG_TYPE "wasm-legalinfo"
 
@@ -19,5 +20,23 @@ using namespace LegalizeActions;
 
 WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
     const WebAssemblySubtarget &ST) {
+  using namespace TargetOpcode;
+
+  const LLT s32 = LLT::scalar(32);
+  const LLT s64 = LLT::scalar(64);
+
+  getActionDefinitionsBuilder({G_CONSTANT, G_ADD, G_AND})
+      .legalFor({s32, s64})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder({G_ASHR, G_SHL})
+      .legalFor({{s32, s32}, {s64, s64}})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64)
+      .scalarSameSizeAs(1, 0);
+
+  getActionDefinitionsBuilder(G_SEXT_INREG).lower();
+
   getLegacyLegalizerInfo().computeTables();
 }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index ff0bc298dc1da..10827acb0b16b 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -13,6 +13,7 @@
 
 #include "WebAssemblyRegisterBankInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "WebAssemblyRegisterInfo.h"
 #include "llvm/CodeGen/TargetRegisterInfo.h"
 
 #define GET_TARGET_REGBANK_IMPL
@@ -20,7 +21,38 @@
 #include "WebAssemblyGenRegisterBank.inc"
 
 namespace llvm {
-namespace WebAssembly {} // namespace WebAssembly
+namespace WebAssembly {
+enum PartialMappingIdx {
+  PMI_None = -1,
+  PMI_I32 = 1,
+  PMI_I64,
+  PMI_Min = PMI_I32,
+};
+
+enum ValueMappingIdx {
+  InvalidIdx = 0,
+  I32Idx = 1,
+  I64Idx = 5,
+};
+
+const RegisterBankInfo::PartialMapping PartMappings[]{{0, 32, I32RegBank},
+                                                      {0, 64, I64RegBank}};
+
+const RegisterBankInfo::ValueMapping ValueMappings[] = {
+    // invalid
+    {nullptr, 0},
+    // up to 4 operands as I32
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    {&PartMappings[PMI_I32 - PMI_Min], 1},
+    // up to 4 operands as I64
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+    {&PartMappings[PMI_I64 - PMI_Min], 1},
+};
+} // namespace WebAssembly
 } // namespace llvm
 
 using namespace llvm;
@@ -30,5 +62,60 @@ WebAssemblyRegisterBankInfo::WebAssemblyRegisterBankInfo(
 
 const RegisterBankInfo::InstructionMapping &
 WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
-  return getInvalidInstructionMapping();
+  unsigned Opc = MI.getOpcode();
+  const MachineFunction &MF = *MI.getParent()->getParent();
+  const MachineRegisterInfo &MRI = MF.getRegInfo();
+
+  if ((Opc != TargetOpcode::COPY && !isPreISelGenericOpcode(Opc)) ||
+      Opc == TargetOpcode::G_PHI) {
+    const RegisterBankInfo::InstructionMapping &Mapping =
+        getInstrMappingImpl(MI);
+    if (Mapping.isValid())
+      return Mapping;
+  }
+
+  using namespace TargetOpcode;
+
+  const unsigned NumOperands = MI.getNumOperands();
+  const ValueMapping *OperandsMapping = nullptr;
+  unsigned MappingID = DefaultMappingID;
+
+  // Check if LLT sizes match sizes of available register banks.
+  for (const MachineOperand &Op : MI.operands()) {
+    if (Op.isReg()) {
+      LLT RegTy = MRI.getType(Op.getReg());
+
+      if (RegTy.isScalar() &&
+          (RegTy.getSizeInBits() != 32 && RegTy.getSizeInBits() != 64))
+        return getInvalidInstructionMapping();
+
+      if (RegTy.isVector() && RegTy.getSizeInBits() != 128)
+        return getInvalidInstructionMapping();
+    }
+  }
+
+  const LLT Op0Ty = MRI.getType(MI.getOperand(0).getReg());
+  unsigned Op0Size = Op0Ty.getSizeInBits();
+
+  auto &Op0IntValueMapping =
+      WebAssembly::ValueMappings[Op0Size == 64 ? WebAssembly::I64Idx
+                                               : WebAssembly::I32Idx];
+
+  switch (Opc) {
+  case G_ADD:
+  case G_AND:
+  case G_ASHR:
+  case G_SHL:
+    OperandsMapping = &Op0IntValueMapping;
+    break;
+  case G_CONSTANT:
+    OperandsMapping = getOperandsMapping({&Op0IntValueMapping, nullptr});
+    break;
+  }
+
+  if (!OperandsMapping)
+    return getInvalidInstructionMapping();
+
+  return getInstructionMapping(MappingID, /*Cost=*/1, OperandsMapping,
+                               NumOperands);
 }
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir
new file mode 100644
index 0000000000000..c1c111606d15f
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/bitwise.mir
@@ -0,0 +1,140 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            and_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: and_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[AND_I32_:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[AND_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %2:i32regbank(s32) = G_AND %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            and_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: and_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64 = ARGUMENT_i64 1, implicit $arguments
+    ; CHECK-NEXT: [[AND_I64_:%[0-9]+]]:i64 = AND_I64 [[ARGUMENT_i64_]], [[ARGUMENT_i64_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[AND_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64(s64) = ARGUMENT_i64 1, implicit $arguments
+    %2:i64regbank(s64) = G_AND %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
+
+---
+name:            shl_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: shl_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 5, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHL_I32_:%[0-9]+]]:i32 = SHL_I32 [[ARGUMENT_i32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHL_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32regbank(s32) = G_CONSTANT i32 5
+    %2:i32regbank(s32) = G_SHL %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            shl_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: shl_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 37, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[ARGUMENT_i64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHL_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64regbank(s64) = G_CONSTANT i64 37
+    %2:i64regbank(s64) = G_SHL %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
+
+---
+name:            ashr_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: ashr_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 5, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHR_S_I32_:%[0-9]+]]:i32 = SHR_S_I32 [[ARGUMENT_i32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHR_S_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32regbank(s32) = G_CONSTANT i32 5
+    %2:i32regbank(s32) = G_ASHR %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            ashr_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: ashr_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 37, implicit-def dead $arguments
+    ; CHECK-NEXT: [[SHR_S_I64_:%[0-9]+]]:i64 = SHR_S_I64 [[ARGUMENT_i64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[SHR_S_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64regbank(s64) = G_CONSTANT i64 37
+    %2:i64regbank(s64) = G_ASHR %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir
new file mode 100644
index 0000000000000..60c916c372636
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instruction-select/int-arithmetic.mir
@@ -0,0 +1,48 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            add_i32
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD_I32_:%[0-9]+]]:i32 = ADD_I32 [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[ADD_I32_]], implicit-def $arguments
+    %0:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %1:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %2:i32regbank(s32) = G_ADD %0, %1
+    RETURN %2(s32), implicit-def $arguments
+...
+
+---
+name:            add_i64
+alignment:       1
+legalized:       true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i64_1:%[0-9]+]]:i64 = ARGUMENT_i64 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD_I64_:%[0-9]+]]:i64 = ADD_I64 [[ARGUMENT_i64_]], [[ARGUMENT_i64_1]], implicit-def dead $arguments
+    ; CHECK-NEXT: RETURN [[ADD_I64_]], implicit-def $arguments
+    %0:i64(s64) = ARGUMENT_i64 0, implicit $arguments
+    %1:i64(s64) = ARGUMENT_i64 1, implicit $arguments
+    %2:i64regbank(s64) = G_ADD %0, %1
+    RETURN %2(s64), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir
new file mode 100644
index 0000000000000..0d12b749e461e
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/legalizer/add.mir
@@ -0,0 +1,200 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -O0 -mtriple=wasm32-unknown-unknown -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            add_i8_zext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_zext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:i32(s32) = G_AND [[ADD]], [[C]]
+    ; CHECK-NEXT: RETURN [[AND]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_ZEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i8_sext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_sext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ADD]], [[C]](s32)
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:i32(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; CHECK-NEXT: RETURN [[ASHR]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_SEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i8_aext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i8_aext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32(s32) = COPY [[ADD]](s32)
+    ; CHECK-NEXT: RETURN [[COPY]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s8) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s8) = G_TRUNC %3(s32)
+    %4:_(s8) = G_ADD %0, %1
+    %5:i32(s32) = G_ANYEXT %4(s8)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_zext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_zext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 65535
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:i32(s32) = G_AND [[ADD]], [[C]]
+    ; CHECK-NEXT: RETURN [[AND]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s16) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s16) = G_TRUNC %3(s32)
+    %4:_(s16) = G_ADD %0, %1
+    %5:i32(s32) = G_ZEXT %4(s16)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_sext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_sext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ADD]], [[C]](s32)
+    ; CHECK-NEXT: [[ASHR:%[0-9]+]]:i32(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; CHECK-NEXT: RETURN [[ASHR]](s32), implicit-def $arguments
+    %2:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    %0:_(s16) = G_TRUNC %2(s32)
+    %3:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(s16) = G_TRUNC %3(s32)
+    %4:_(s16) = G_ADD %0, %1
+    %5:i32(s32) = G_SEXT %4(s16)
+    RETURN %5(s32), implicit-def $arguments
+...
+
+---
+name:            add_i16_aext
+alignment:       1
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; CHECK-LABEL: name: add_i16_aext
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32(s32) = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_1:%[0-9]+]]:i32(s32) = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ARGUMENT_i32_]], [[ARGUMENT_i32_1]]
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:i32(s32) = COPY [[ADD]](s32)
+    ; CHE...
[truncated]

@QuantumSegfault
Copy link
Contributor Author

These additions are over 80% test...

I've seen regbankselect implemented a couple different ways. I went with something more similar to Mips and RISCV.

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

🪟 Windows x64 Test Results

  • 131489 tests passed
  • 2955 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

LLVM

LLVM.ThinLTO/X86/dtlto/imports.ll
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 3
rm -rf C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp && split-file C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp && cd C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp
# executed command: rm -rf 'C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp'
# note: command had no output on stdout or stderr
# executed command: split-file 'C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll' 'C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp'
# note: command had no output on stdout or stderr
# executed command: cd 'C:\_work\llvm-project\llvm-project\build\test\ThinLTO\X86\dtlto\Output\imports.ll.tmp'
# note: command had no output on stdout or stderr
# RUN: at line 6
c:\_work\llvm-project\llvm-project\build\bin\opt.exe -thinlto-bc 0.ll -o 0.bc -O2
# executed command: 'c:\_work\llvm-project\llvm-project\build\bin\opt.exe' -thinlto-bc 0.ll -o 0.bc -O2
# note: command had no output on stdout or stderr
# RUN: at line 7
c:\_work\llvm-project\llvm-project\build\bin\opt.exe -thinlto-bc 1.ll -o 1.bc -O2
# executed command: 'c:\_work\llvm-project\llvm-project\build\bin\opt.exe' -thinlto-bc 1.ll -o 1.bc -O2
# note: command had no output on stdout or stderr
# RUN: at line 20
not c:\_work\llvm-project\llvm-project\build\bin\llvm-lto2.exe run 0.bc 1.bc -o t.o -dtlto-distributor="C:\Python312\python.exe" -dtlto-distributor-arg=C:\_work\llvm-project\llvm-project\llvm/utils/dtlto/validate.py -r=0.bc,g,px -r=1.bc,f,px -r=1.bc,g 2>&1 | c:\_work\llvm-project\llvm-project\build\bin\filecheck.exe C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll --check-prefixes=INPUTS,ERR
# executed command: not 'c:\_work\llvm-project\llvm-project\build\bin\llvm-lto2.exe' run 0.bc 1.bc -o t.o '-dtlto-distributor=C:\Python312\python.exe' '-dtlto-distributor-arg=C:\_work\llvm-project\llvm-project\llvm/utils/dtlto/validate.py' -r=0.bc,g,px -r=1.bc,f,px -r=1.bc,g
# note: command had no output on stdout or stderr
# executed command: 'c:\_work\llvm-project\llvm-project\build\bin\filecheck.exe' 'C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll' --check-prefixes=INPUTS,ERR
# note: command had no output on stdout or stderr
# RUN: at line 41
not c:\_work\llvm-project\llvm-project\build\bin\llvm-lto2.exe run 0.bc 1.bc -o t.o -dtlto-distributor="C:\Python312\python.exe" -dtlto-distributor-arg=C:\_work\llvm-project\llvm-project\llvm/utils/dtlto/validate.py -r=0.bc,g,px -r=1.bc,f,px -r=1.bc,g -save-temps 2>&1    | c:\_work\llvm-project\llvm-project\build\bin\filecheck.exe C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll --check-prefixes=ERR
# executed command: not 'c:\_work\llvm-project\llvm-project\build\bin\llvm-lto2.exe' run 0.bc 1.bc -o t.o '-dtlto-distributor=C:\Python312\python.exe' '-dtlto-distributor-arg=C:\_work\llvm-project\llvm-project\llvm/utils/dtlto/validate.py' -r=0.bc,g,px -r=1.bc,f,px -r=1.bc,g -save-temps
# note: command had no output on stdout or stderr
# executed command: 'c:\_work\llvm-project\llvm-project\build\bin\filecheck.exe' 'C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll' --check-prefixes=ERR
# .---command stderr------------
# | C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll:37:6: error: ERR: expected string not found in input
# | ERR: failed: DTLTO backend compilation: cannot open native object file:
# |      ^
# | <stdin>:1:1: note: scanning from here
# | llvm-lto2: LTO::run failed: 'cannot open 1.2.61756.native.o.thinlto.bc': invalid argument
# | ^
# | <stdin>:1:2: note: possible intended match here
# | llvm-lto2: LTO::run failed: 'cannot open 1.2.61756.native.o.thinlto.bc': invalid argument
# |  ^
# | 
# | Input file: <stdin>
# | Check file: C:\_work\llvm-project\llvm-project\llvm\test\ThinLTO\X86\dtlto\imports.ll
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             1: llvm-lto2: LTO::run failed: 'cannot open 1.2.61756.native.o.thinlto.bc': invalid argument 
# | check:37'0     X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found
# | check:37'1      ?                                                                                         possible intended match
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@github-actions
Copy link

github-actions bot commented Feb 27, 2026

🐧 Linux x64 Test Results

  • 191234 tests passed
  • 4971 tests skipped

✅ The build succeeded and all tests passed.

@QuantumSegfault QuantumSegfault force-pushed the wasm-gisel-full-pipeline-integers branch 2 times, most recently from 34c6558 to 811bf11 Compare February 27, 2026 07:40
@QuantumSegfault QuantumSegfault force-pushed the wasm-gisel-full-pipeline-integers branch from 811bf11 to f2b76e2 Compare February 27, 2026 07:40
Comment on lines 89 to 94
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of doing this? This code shouldn't really need to defend against illegal types?

Copy link
Contributor Author

@QuantumSegfault QuantumSegfault Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good sanity check. Helps make mistakes in the legalizer (particularly custom lowerings) more evident, rather than letting them past regbankselect (e.g. assigning s8 a i32regbank) and then failing further down the line.

Guard behind #ifndef NDEBUG maybe? Similar to the MF legality checks done before attempting regbankselect?

@QuantumSegfault QuantumSegfault changed the title [WebAssembly][GlobalISel] G_ADD legalization & selection [WebAssembly][GlobalISel] G_ADD (and in-reg ext/trunc related) legalization & selection Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants