[llvm] [WebAssembly][GlobalISel] Implement pointer and memory ops. (PR #206885)

Demetrius Kanios via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 4 01:05:08 PDT 2026


https://github.com/QuantumSegfault updated https://github.com/llvm/llvm-project/pull/206885

>From 79f2eeb1a00ce544a3e436333efbfcd355d99da7 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 30 Jun 2026 21:21:40 -0700
Subject: [PATCH 1/3] Implement pointer and memory ops.

---
 .../GISel/WebAssemblyInstructionSelector.cpp  | 142 ++++++++++-
 .../GISel/WebAssemblyLegalizerInfo.cpp        |  74 +++++-
 .../GISel/WebAssemblyRegisterBankInfo.cpp     |   2 +-
 .../Target/WebAssembly/WebAssemblyGISel.td    |  29 +++
 .../GlobalISel/instructions/constant.ll       |  20 +-
 .../GlobalISel/instructions/frame_index.ll    |  35 +++
 .../GlobalISel/instructions/global_value.ll   | 127 ++++++++++
 .../GlobalISel/instructions/implicit_def.mir  |  25 +-
 .../GlobalISel/instructions/inttoptr.ll       |  79 ++++++
 .../GlobalISel/instructions/load.ll           | 198 +++++++++++++++
 .../GlobalISel/instructions/ptradd.ll         |  91 +++++++
 .../GlobalISel/instructions/ptrmask.ll        |  17 ++
 .../instructions/ptrmask_wasm32.mir           |  85 +++++++
 .../instructions/ptrmask_wasm64.mir           |  87 +++++++
 .../GlobalISel/instructions/ptrtoint.ll       |  71 ++++++
 .../GlobalISel/instructions/sextload.mir      | 220 ++++++++++++++++
 .../GlobalISel/instructions/store.ll          | 236 ++++++++++++++++++
 .../GlobalISel/instructions/zextload.mir      | 208 +++++++++++++++
 18 files changed, 1736 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/frame_index.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/global_value.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/inttoptr.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/load.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptradd.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm32.mir
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm64.mir
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrtoint.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/sextload.mir
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/store.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/zextload.mir

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 2331b8b636603..3709327170ac6 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -13,6 +13,8 @@
 
 #include "GISel/WebAssemblyRegisterBankInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "Utils/WasmAddressSpaces.h"
+#include "Utils/WebAssemblyTypeUtilities.h"
 #include "WebAssemblyRegisterInfo.h"
 #include "WebAssemblySubtarget.h"
 #include "WebAssemblyTargetMachine.h"
@@ -42,14 +44,23 @@ class WebAssemblyInstructionSelector : public InstructionSelector {
 
   bool select(MachineInstr &I) override;
 
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands32(MachineOperand &Root) const;
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands64(MachineOperand &Root) const;
+
   static const char *getName() { return DEBUG_TYPE; }
 
 private:
   bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
   bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const;
 
+  InstructionSelector::ComplexRendererFns
+  selectAddrOperands(LLT AddrType, unsigned int ConstOpc,
+                     MachineOperand &Root) const;
+
   const WebAssemblyTargetMachine &TM;
-  // const WebAssemblySubtarget &STI;
+  const WebAssemblySubtarget &STI;
   const WebAssemblyInstrInfo &TII;
   const WebAssemblyRegisterInfo &TRI;
   const WebAssemblyRegisterBankInfo &RBI;
@@ -72,8 +83,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 +95,32 @@ WebAssemblyInstructionSelector::WebAssemblyInstructionSelector(
 {
 }
 
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
+                                                   unsigned int ConstOpc,
+                                                   MachineOperand &Root) const {
+
+  if (!Root.isReg())
+    return std::nullopt;
+
+  return {{
+      [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
+      [=](MachineInstrBuilder &MIB) { MIB.addReg(Root.getReg()); },
+  }};
+}
+
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands32(
+    MachineOperand &Root) const {
+  return selectAddrOperands(LLT::integer(32), WebAssembly::CONST_I32, Root);
+}
+
+InstructionSelector::ComplexRendererFns
+WebAssemblyInstructionSelector::selectAddrOperands64(
+    MachineOperand &Root) const {
+  return selectAddrOperands(LLT::integer(64), WebAssembly::CONST_I64, Root);
+}
+
 bool WebAssemblyInstructionSelector::selectCopy(
     MachineInstr &I, MachineRegisterInfo &MRI) const {
   const TargetRegisterClass *DstRC =
@@ -140,6 +177,7 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
   MachineBasicBlock &MBB = *I.getParent();
   MachineFunction &MF = *MBB.getParent();
   MachineRegisterInfo &MRI = MF.getRegInfo();
+  const TargetLowering &TLI = *STI.getTargetLowering();
 
   if (!I.isPreISelOpcode()) {
     if (I.isCopy())
@@ -152,6 +190,9 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
 
   using namespace TargetOpcode;
 
+  auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
+  auto PtrIsI64 = PointerWidth == 64;
+
   switch (I.getOpcode()) {
   case G_IMPLICIT_DEF: {
     const Register DefReg = I.getOperand(0).getReg();
@@ -165,6 +206,101 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
     I.setDesc(TII.get(TargetOpcode::IMPLICIT_DEF));
     return RBI.constrainGenericRegister(DefReg, *DefRC, MRI) != nullptr;
   }
+  case G_PTRTOINT: {
+    assert(MRI.getType(I.getOperand(1).getReg()).isPointer() &&
+           "G_PTRTOINT selection with non-pointer?");
+
+    I.setDesc(
+        TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+
+    return true;
+  }
+  case G_INTTOPTR: {
+    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+           "G_INTTOPTR selection with non-pointer?");
+
+    I.setDesc(
+        TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+
+    return true;
+  }
+  case G_PTRMASK: {
+    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
+           "G_PTRMASK selection with non-pointer?");
+
+    I.setDesc(TII.get(PtrIsI64 ? WebAssembly::AND_I64 : WebAssembly::AND_I32));
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+
+    return true;
+  }
+  case G_GLOBAL_VALUE: {
+    assert(I.getOperand(1).getTargetFlags() == 0 &&
+           "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
+    assert(WebAssembly::isValidAddressSpace(
+               MRI.getType(I.getOperand(0).getReg()).getAddressSpace()) &&
+           "Invalid address space for WebAssembly target");
+
+    unsigned OperandFlags = 0;
+    const llvm::GlobalValue *GV = I.getOperand(1).getGlobal();
+
+    if (TLI.isPositionIndependent()) {
+      if (TM.shouldAssumeDSOLocal(GV)) {
+        const char *BaseName;
+        if (GV->getValueType()->isFunctionTy()) {
+          BaseName = MF.createExternalSymbolName("__table_base");
+          OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL;
+        } else {
+          BaseName = MF.createExternalSymbolName("__memory_base");
+          OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL;
+        }
+        MachineIRBuilder B(I);
+
+        auto MemBase =
+            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(MemBase, PtrIsI64 ? &WebAssembly::I64RegClass
+                                          : &WebAssembly::I32RegClass);
+        auto Offset =
+            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
+        MRI.setRegClass(Offset, PtrIsI64 ? &WebAssembly::I64RegClass
+                                         : &WebAssembly::I32RegClass);
+
+        B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
+                              : WebAssembly::GLOBAL_GET_I32)
+            .addDef(MemBase)
+            .addExternalSymbol(BaseName);
+
+        B.buildInstr(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32)
+            .addDef(Offset)
+            .addGlobalAddress(GV, I.getOperand(1).getOffset(), OperandFlags);
+
+        auto MIB =
+            B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
+                .addDef(I.getOperand(0).getReg())
+                .addReg(MemBase)
+                .addReg(Offset);
+        constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI);
+
+        I.eraseFromParent();
+        return true;
+      }
+      OperandFlags = WebAssemblyII::MO_GOT;
+    }
+
+    auto NewOpc = PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
+
+    if (OperandFlags & WebAssemblyII::MO_GOT) {
+      NewOpc =
+          PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 : WebAssembly::GLOBAL_GET_I32;
+    }
+
+    I.setDesc(TII.get(NewOpc));
+    I.getOperand(1).setTargetFlags(OperandFlags);
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+
+    return true;
+  }
   default:
     break;
   }
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 956432a78fb50..8ceebac8c4c72 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -25,6 +25,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
     const WebAssemblySubtarget &ST) {
   using namespace TargetOpcode;
 
+  const LLT i8 = LLT::integer(8);
+  const LLT i16 = LLT::integer(16);
   const LLT i32 = LLT::integer(32);
   const LLT i64 = LLT::integer(64);
 
@@ -34,13 +36,22 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
   const LLT s32 = LLT::scalar(32);
   const LLT s64 = LLT::scalar(64);
 
+  const LLT p0 = LLT::pointer(0, ST.hasAddr64() ? 64 : 32);
+  const LLT p0i = LLT::integer(ST.hasAddr64() ? 64 : 32);
+  const LLT p0s = LLT::scalar(ST.hasAddr64() ? 64 : 32);
+
   getActionDefinitionsBuilder(G_IMPLICIT_DEF)
-      .legalFor({i32, i64, f32, f64})
+      .legalFor({i32, i64, f32, f64, p0})
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
 
-  getActionDefinitionsBuilder({G_CONSTANT, G_ADD, G_SUB, G_MUL, G_UDIV, G_SDIV,
-                               G_UREM, G_SREM, G_AND, G_OR, G_XOR})
+  getActionDefinitionsBuilder(G_CONSTANT)
+      .legalFor({i32, i64, p0})
+      .widenScalarToNextPow2(0)
+      .clampScalar(0, s32, s64);
+
+  getActionDefinitionsBuilder(
+      {G_ADD, G_SUB, G_MUL, G_UDIV, G_SDIV, G_UREM, G_SREM, G_AND, G_OR, G_XOR})
       .legalFor({i32, i64})
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64);
@@ -146,6 +157,63 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .widenScalarToNextPow2(0)
       .clampScalar(0, s32, s64)
       .clampScalar(1, s32, s32);
+
+  getActionDefinitionsBuilder(G_PTRTOINT)
+      .legalFor({{p0i, p0}})
+      .clampScalar(0, p0s, p0s);
+  getActionDefinitionsBuilder(G_INTTOPTR)
+      .legalFor({{p0, p0i}})
+      .clampScalar(1, p0s, p0s);
+
+  getActionDefinitionsBuilder({G_PTR_ADD, G_PTRMASK})
+      .legalFor({{p0, p0i}})
+      .clampScalar(1, p0s, p0s);
+
+  getActionDefinitionsBuilder({G_FRAME_INDEX, G_GLOBAL_VALUE}).legalFor({p0});
+
+  getActionDefinitionsBuilder(G_LOAD)
+      .legalForTypesWithMemDesc({{i32, p0, i8, 1},
+                                 {i32, p0, i16, 1},
+                                 {i32, p0, i32, 1},
+
+                                 {i64, p0, i8, 1},
+                                 {i64, p0, i16, 1},
+                                 {i64, p0, i32, 1},
+                                 {i64, p0, i64, 1},
+
+                                 {f32, p0, f32, 1},
+                                 {f64, p0, f64, 1},
+
+                                 {p0, p0, p0, 1}})
+      .clampScalar(0, s32, s64)
+      .lowerIfMemSizeNotByteSizePow2();
+
+  getActionDefinitionsBuilder({G_ZEXTLOAD, G_SEXTLOAD})
+      .legalForTypesWithMemDesc({{i32, p0, i8, 1},
+                                 {i32, p0, i16, 1},
+
+                                 {i64, p0, i8, 1},
+                                 {i64, p0, i16, 1},
+                                 {i64, p0, i32, 1}})
+      .clampScalar(0, s32, s64)
+      .lowerIfMemSizeNotByteSizePow2();
+
+  getActionDefinitionsBuilder(G_STORE)
+      .legalForTypesWithMemDesc({{i32, p0, i8, 1},
+                                 {i32, p0, i16, 1},
+                                 {i32, p0, i32, 1},
+
+                                 {i64, p0, i8, 1},
+                                 {i64, p0, i16, 1},
+                                 {i64, p0, s32, 1},
+                                 {i64, p0, i64, 1},
+
+                                 {f32, p0, f32, 1},
+                                 {f64, p0, f64, 1},
+
+                                 {p0, p0, p0, 1}})
+      .clampScalar(0, s32, s64)
+      .lowerIfMemSizeNotByteSizePow2();
 }
 
 bool WebAssemblyLegalizerInfo::legalizeCustom(
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
index 7e50ca6ab22fe..78f2f3d3fae03 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyRegisterBankInfo.cpp
@@ -77,7 +77,7 @@ WebAssemblyRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
 
     OpSize[Idx] = Ty.getSizeInBits().getKnownMinValue();
 
-    if (Ty.isInteger()) {
+    if (Ty.isInteger() || (Ty.isPointer() && Ty.getAddressSpace() == 0)) {
       if (OpSize[Idx] == 32) {
         OpRegBankIdx[Idx] = WebAssembly::PMI_I32;
       } else if (OpSize[Idx] == 64) {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
index 0e49d61deca67..640bd279cb20b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyGISel.td
@@ -15,3 +15,32 @@
 
 include "WebAssembly.td"
 include "WebAssemblyCombine.td"
+
+defvar ModeAddr32 = DefaultMode;
+def ModeAddr64 : HwMode<[HasAddr64]>;
+
+def p0i : ValueTypeByHwMode<[ModeAddr32, ModeAddr64],
+                            [i32, i64]>;
+def p0 : PtrValueTypeByHwMode<p0i, 0>;
+
+
+def : Pat<(p0 (ptradd p0:$lhs, p0i:$rhs)),
+          (ADD_I32 I32:$lhs, I32:$rhs)>, Requires<[HasAddr32]>;
+def : Pat<(p0 (ptradd p0:$lhs, p0i:$rhs)),
+          (ADD_I64 I64:$lhs, I64:$rhs)>, Requires<[HasAddr64]>;
+
+def : Pat<(frameindex:$fi),
+          (COPY_I32 (to_tframeindex $fi))>, Requires<[HasAddr32]>;
+def : Pat<(frameindex:$fi),
+          (COPY_I64 (to_tframeindex $fi))>, Requires<[HasAddr64]>;
+
+
+//===----------------------------------------------------------------------===//
+// Complex pattern equivalents
+//===----------------------------------------------------------------------===//
+
+def gi_AddrOps32 : GIComplexOperandMatcher<s32, "selectAddrOperands32">,
+                   GIComplexPatternEquiv<AddrOps32>;
+
+def gi_AddrOps64 : GIComplexOperandMatcher<s64, "selectAddrOperands64">,
+                   GIComplexPatternEquiv<AddrOps64>;
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/constant.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/constant.ll
index 8ceda5bb3740a..7e74d6035afa4 100644
--- a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/constant.ll
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/constant.ll
@@ -1,7 +1,7 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc < %s -O0 --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefixes=CHECK,WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefixes=CHECK,WASM64
 
-target triple = "wasm32-unknown-unknown"
 
 define i1 @const_i1() {
 ; CHECK-LABEL: const_i1:
@@ -83,3 +83,19 @@ define i64 @const_i64() {
 ; CHECK-NEXT:    return $pop0
   ret i64 18446744073709551615
 }
+
+
+define ptr @const_p0() {
+; WASM32-LABEL: const_p0:
+; WASM32:         .functype const_p0 () -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    i32.const $push0=, 0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: const_p0:
+; WASM64:         .functype const_p0 () -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    i64.const $push0=, 0
+; WASM64-NEXT:    return $pop0
+  ret ptr null
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/frame_index.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/frame_index.ll
new file mode 100644
index 0000000000000..998adf5ce5885
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/frame_index.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define ptr @frame_index() {
+; WASM32-LABEL: frame_index:
+; WASM32:         .functype frame_index () -> (i32)
+; WASM32-NEXT:    .local i32
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    global.get $push1=, __stack_pointer
+; WASM32-NEXT:    i32.const $push2=, 16
+; WASM32-NEXT:    i32.sub $push4=, $pop1, $pop2
+; WASM32-NEXT:    local.set 0, $pop4
+; WASM32-NEXT:    local.get $push5=, 0
+; WASM32-NEXT:    i32.const $push3=, 14
+; WASM32-NEXT:    i32.add $push0=, $pop5, $pop3
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: frame_index:
+; WASM64:         .functype frame_index () -> (i64)
+; WASM64-NEXT:    .local i64
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    global.get $push1=, __stack_pointer
+; WASM64-NEXT:    i64.const $push2=, 16
+; WASM64-NEXT:    i64.sub $push4=, $pop1, $pop2
+; WASM64-NEXT:    local.set 0, $pop4
+; WASM64-NEXT:    local.get $push5=, 0
+; WASM64-NEXT:    i64.const $push3=, 14
+; WASM64-NEXT:    i64.add $push0=, $pop5, $pop3
+; WASM64-NEXT:    return $pop0
+  %a = alloca i16
+  %b = alloca double
+
+  ret ptr %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/global_value.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/global_value.ll
new file mode 100644
index 0000000000000..481f4e84a5967
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/global_value.ll
@@ -0,0 +1,127 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -relocation-model=pic -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32-PIC
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -relocation-model=pic -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64-PIC
+
+ at globl = global i32 0
+declare void @func()
+
+ at globl_local = dso_local global i32 0
+declare dso_local void @func_local()
+
+define ptr @global_value_globl() {
+; WASM32-LABEL: global_value_globl:
+; WASM32:         .functype global_value_globl () -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    i32.const $push0=, globl
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: global_value_globl:
+; WASM64:         .functype global_value_globl () -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    i64.const $push0=, globl
+; WASM64-NEXT:    return $pop0
+;
+; WASM32-PIC-LABEL: global_value_globl:
+; WASM32-PIC:         .functype global_value_globl () -> (i32)
+; WASM32-PIC-NEXT:  # %bb.0:
+; WASM32-PIC-NEXT:    global.get $push0=, globl at GOT
+; WASM32-PIC-NEXT:    return $pop0
+;
+; WASM64-PIC-LABEL: global_value_globl:
+; WASM64-PIC:         .functype global_value_globl () -> (i64)
+; WASM64-PIC-NEXT:  # %bb.0:
+; WASM64-PIC-NEXT:    global.get $push0=, globl at GOT
+; WASM64-PIC-NEXT:    return $pop0
+  ret ptr @globl
+}
+
+define ptr @global_value_func() {
+; WASM32-LABEL: global_value_func:
+; WASM32:         .functype global_value_func () -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    i32.const $push0=, func
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: global_value_func:
+; WASM64:         .functype global_value_func () -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    i64.const $push0=, func
+; WASM64-NEXT:    return $pop0
+;
+; WASM32-PIC-LABEL: global_value_func:
+; WASM32-PIC:         .functype global_value_func () -> (i32)
+; WASM32-PIC-NEXT:  # %bb.0:
+; WASM32-PIC-NEXT:    global.get $push0=, func at GOT
+; WASM32-PIC-NEXT:    return $pop0
+;
+; WASM64-PIC-LABEL: global_value_func:
+; WASM64-PIC:         .functype global_value_func () -> (i64)
+; WASM64-PIC-NEXT:  # %bb.0:
+; WASM64-PIC-NEXT:    global.get $push0=, func at GOT
+; WASM64-PIC-NEXT:    return $pop0
+  ret ptr @func
+}
+
+define ptr @global_value_globl_local() {
+; WASM32-LABEL: global_value_globl_local:
+; WASM32:         .functype global_value_globl_local () -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    i32.const $push0=, globl_local
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: global_value_globl_local:
+; WASM64:         .functype global_value_globl_local () -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    i64.const $push0=, globl_local
+; WASM64-NEXT:    return $pop0
+;
+; WASM32-PIC-LABEL: global_value_globl_local:
+; WASM32-PIC:         .functype global_value_globl_local () -> (i32)
+; WASM32-PIC-NEXT:  # %bb.0:
+; WASM32-PIC-NEXT:    global.get $push1=, __memory_base
+; WASM32-PIC-NEXT:    i32.const $push2=, globl_local at MBREL
+; WASM32-PIC-NEXT:    i32.add $push0=, $pop1, $pop2
+; WASM32-PIC-NEXT:    return $pop0
+;
+; WASM64-PIC-LABEL: global_value_globl_local:
+; WASM64-PIC:         .functype global_value_globl_local () -> (i64)
+; WASM64-PIC-NEXT:  # %bb.0:
+; WASM64-PIC-NEXT:    global.get $push1=, __memory_base
+; WASM64-PIC-NEXT:    i64.const $push2=, globl_local at MBREL
+; WASM64-PIC-NEXT:    i64.add $push0=, $pop1, $pop2
+; WASM64-PIC-NEXT:    return $pop0
+  ret ptr @globl_local
+}
+
+define ptr @global_value_func_local() {
+; WASM32-LABEL: global_value_func_local:
+; WASM32:         .functype global_value_func_local () -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    i32.const $push0=, func_local
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: global_value_func_local:
+; WASM64:         .functype global_value_func_local () -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    i64.const $push0=, func_local
+; WASM64-NEXT:    return $pop0
+;
+; WASM32-PIC-LABEL: global_value_func_local:
+; WASM32-PIC:         .functype global_value_func_local () -> (i32)
+; WASM32-PIC-NEXT:  # %bb.0:
+; WASM32-PIC-NEXT:    global.get $push1=, __table_base
+; WASM32-PIC-NEXT:    i32.const $push2=, func_local at TBREL
+; WASM32-PIC-NEXT:    i32.add $push0=, $pop1, $pop2
+; WASM32-PIC-NEXT:    return $pop0
+;
+; WASM64-PIC-LABEL: global_value_func_local:
+; WASM64-PIC:         .functype global_value_func_local () -> (i64)
+; WASM64-PIC-NEXT:  # %bb.0:
+; WASM64-PIC-NEXT:    global.get $push1=, __table_base
+; WASM64-PIC-NEXT:    i64.const $push2=, func_local at TBREL
+; WASM64-PIC-NEXT:    i64.add $push0=, $pop1, $pop2
+; WASM64-PIC-NEXT:    return $pop0
+  ret ptr @func_local
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/implicit_def.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/implicit_def.mir
index 3498ff229ca81..3f5406f78ed74 100644
--- a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/implicit_def.mir
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/implicit_def.mir
@@ -1,5 +1,6 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s
+# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=CHECK,WASM32
+# RUN: llc -mtriple=wasm64-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=CHECK,WASM64
 
 ---
 name:            implicit_def_i8
@@ -98,3 +99,25 @@ body:             |
     %0:_(f64) = G_IMPLICIT_DEF
     RETURN %0(f64), implicit-def $arguments
 ...
+
+---
+name:            implicit_def_p0
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: implicit_def_p0
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: RETURN [[DEF]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: implicit_def_p0
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: RETURN [[DEF]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    RETURN %0(p0), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/inttoptr.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/inttoptr.ll
new file mode 100644
index 0000000000000..8aaa8b400c7b5
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/inttoptr.ll
@@ -0,0 +1,79 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define ptr @inttoptr_i8(i8 %x) {
+; WASM32-LABEL: inttoptr_i8:
+; WASM32:         .functype inttoptr_i8 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push2=, 0
+; WASM32-NEXT:    i32.const $push1=, 255
+; WASM32-NEXT:    i32.and $push0=, $pop2, $pop1
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: inttoptr_i8:
+; WASM64:         .functype inttoptr_i8 (i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push3=, 0
+; WASM64-NEXT:    i64.extend_i32_u $push1=, $pop3
+; WASM64-NEXT:    i64.const $push2=, 255
+; WASM64-NEXT:    i64.and $push0=, $pop1, $pop2
+; WASM64-NEXT:    return $pop0
+  %a = inttoptr i8 %x to ptr
+  ret ptr %a
+}
+
+define ptr @inttoptr_i16(i16 %x) {
+; WASM32-LABEL: inttoptr_i16:
+; WASM32:         .functype inttoptr_i16 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push2=, 0
+; WASM32-NEXT:    i32.const $push1=, 65535
+; WASM32-NEXT:    i32.and $push0=, $pop2, $pop1
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: inttoptr_i16:
+; WASM64:         .functype inttoptr_i16 (i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push3=, 0
+; WASM64-NEXT:    i64.extend_i32_u $push1=, $pop3
+; WASM64-NEXT:    i64.const $push2=, 65535
+; WASM64-NEXT:    i64.and $push0=, $pop1, $pop2
+; WASM64-NEXT:    return $pop0
+  %a = inttoptr i16 %x to ptr
+  ret ptr %a
+}
+
+define ptr @inttoptr_i32(i32 %x) {
+; WASM32-LABEL: inttoptr_i32:
+; WASM32:         .functype inttoptr_i32 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push0=, 0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: inttoptr_i32:
+; WASM64:         .functype inttoptr_i32 (i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i64.extend_i32_u $push0=, $pop1
+; WASM64-NEXT:    return $pop0
+  %a = inttoptr i32 %x to ptr
+  ret ptr %a
+}
+
+define ptr @inttoptr_i64(i64 %x) {
+; WASM32-LABEL: inttoptr_i64:
+; WASM32:         .functype inttoptr_i64 (i64) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.wrap_i64 $push0=, $pop1
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: inttoptr_i64:
+; WASM64:         .functype inttoptr_i64 (i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push0=, 0
+; WASM64-NEXT:    return $pop0
+  %a = inttoptr i64 %x to ptr
+  ret ptr %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/load.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/load.ll
new file mode 100644
index 0000000000000..cfed662c7a333
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/load.ll
@@ -0,0 +1,198 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define i1 @load_i1(ptr %x) {
+; WASM32-LABEL: load_i1:
+; WASM32:         .functype load_i1 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load8_u $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i1:
+; WASM64:         .functype load_i1 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.load8_u $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load i1, ptr %x
+  ret i1 %a
+}
+
+define i8 @load_i8(ptr %x) {
+; WASM32-LABEL: load_i8:
+; WASM32:         .functype load_i8 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load8_u $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i8:
+; WASM64:         .functype load_i8 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.load8_u $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load i8, ptr %x
+  ret i8 %a
+}
+
+define i16 @load_i16(ptr %x) {
+; WASM32-LABEL: load_i16:
+; WASM32:         .functype load_i16 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load16_u $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i16:
+; WASM64:         .functype load_i16 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.load16_u $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load i16, ptr %x
+  ret i16 %a
+}
+
+define i32 @load_i32(ptr %x) {
+; WASM32-LABEL: load_i32:
+; WASM32:         .functype load_i32 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i32:
+; WASM64:         .functype load_i32 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.load $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load i32, ptr %x
+  ret i32 %a
+}
+
+define i32 @load_i32_align1(ptr %x) {
+; WASM32-LABEL: load_i32_align1:
+; WASM32:         .functype load_i32_align1 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load $push0=, 0($pop1):p2align=0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i32_align1:
+; WASM64:         .functype load_i32_align1 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.load $push0=, 0($pop1):p2align=0
+; WASM64-NEXT:    return $pop0
+  %a = load i32, ptr %x, align 1
+  ret i32 %a
+}
+
+define i48 @load_i48(ptr %x) {
+; WASM32-LABEL: load_i48:
+; WASM32:         .functype load_i48 (i32) -> (i64)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push7=, 0
+; WASM32-NEXT:    i64.load32_u $push0=, 0($pop7)
+; WASM32-NEXT:    local.get $push8=, 0
+; WASM32-NEXT:    i32.const $push1=, 4
+; WASM32-NEXT:    i32.add $push2=, $pop8, $pop1
+; WASM32-NEXT:    i64.load16_u $push3=, 0($pop2)
+; WASM32-NEXT:    i64.const $push4=, 32
+; WASM32-NEXT:    i64.shl $push5=, $pop3, $pop4
+; WASM32-NEXT:    i64.or $push6=, $pop0, $pop5
+; WASM32-NEXT:    return $pop6
+;
+; WASM64-LABEL: load_i48:
+; WASM64:         .functype load_i48 (i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push7=, 0
+; WASM64-NEXT:    i64.load32_u $push0=, 0($pop7)
+; WASM64-NEXT:    local.get $push8=, 0
+; WASM64-NEXT:    i64.const $push1=, 4
+; WASM64-NEXT:    i64.add $push2=, $pop8, $pop1
+; WASM64-NEXT:    i64.load16_u $push3=, 0($pop2)
+; WASM64-NEXT:    i64.const $push4=, 32
+; WASM64-NEXT:    i64.shl $push5=, $pop3, $pop4
+; WASM64-NEXT:    i64.or $push6=, $pop0, $pop5
+; WASM64-NEXT:    return $pop6
+  %a = load i48, ptr %x
+  ret i48 %a
+}
+
+define i64 @load_i64(ptr %x) {
+; WASM32-LABEL: load_i64:
+; WASM32:         .functype load_i64 (i32) -> (i64)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i64.load $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_i64:
+; WASM64:         .functype load_i64 (i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i64.load $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load i64, ptr %x
+  ret i64 %a
+}
+
+
+define float @load_f32(ptr %x) {
+; WASM32-LABEL: load_f32:
+; WASM32:         .functype load_f32 (i32) -> (f32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    f32.load $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_f32:
+; WASM64:         .functype load_f32 (i64) -> (f32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    f32.load $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load float, ptr %x
+  ret float %a
+}
+
+define double @load_f64(ptr %x) {
+; WASM32-LABEL: load_f64:
+; WASM32:         .functype load_f64 (i32) -> (f64)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    f64.load $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_f64:
+; WASM64:         .functype load_f64 (i64) -> (f64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    f64.load $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load double, ptr %x
+  ret double %a
+}
+
+define ptr @load_ptr(ptr %x) {
+; WASM32-LABEL: load_ptr:
+; WASM32:         .functype load_ptr (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i32.load $push0=, 0($pop1)
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: load_ptr:
+; WASM64:         .functype load_ptr (i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i64.load $push0=, 0($pop1)
+; WASM64-NEXT:    return $pop0
+  %a = load ptr, ptr %x
+  ret ptr %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptradd.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptradd.ll
new file mode 100644
index 0000000000000..44083d110c8e4
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptradd.ll
@@ -0,0 +1,91 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define ptr @ptradd_i8(ptr %x, i8 %y) {
+; WASM32-LABEL: ptradd_i8:
+; WASM32:         .functype ptradd_i8 (i32, i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push3=, 0
+; WASM32-NEXT:    local.get $push2=, 1
+; WASM32-NEXT:    i32.extend8_s $push0=, $pop2
+; WASM32-NEXT:    i32.add $push1=, $pop3, $pop0
+; WASM32-NEXT:    return $pop1
+;
+; WASM64-LABEL: ptradd_i8:
+; WASM64:         .functype ptradd_i8 (i64, i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push4=, 0
+; WASM64-NEXT:    local.get $push3=, 1
+; WASM64-NEXT:    i64.extend_i32_u $push2=, $pop3
+; WASM64-NEXT:    i64.extend8_s $push0=, $pop2
+; WASM64-NEXT:    i64.add $push1=, $pop4, $pop0
+; WASM64-NEXT:    return $pop1
+  %a = getelementptr i8, ptr %x, i8 %y
+  ret ptr %a
+}
+
+define ptr @ptradd_i16(ptr %x, i16 %y) {
+; WASM32-LABEL: ptradd_i16:
+; WASM32:         .functype ptradd_i16 (i32, i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push3=, 0
+; WASM32-NEXT:    local.get $push2=, 1
+; WASM32-NEXT:    i32.extend16_s $push0=, $pop2
+; WASM32-NEXT:    i32.add $push1=, $pop3, $pop0
+; WASM32-NEXT:    return $pop1
+;
+; WASM64-LABEL: ptradd_i16:
+; WASM64:         .functype ptradd_i16 (i64, i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push4=, 0
+; WASM64-NEXT:    local.get $push3=, 1
+; WASM64-NEXT:    i64.extend_i32_u $push2=, $pop3
+; WASM64-NEXT:    i64.extend16_s $push0=, $pop2
+; WASM64-NEXT:    i64.add $push1=, $pop4, $pop0
+; WASM64-NEXT:    return $pop1
+  %a = getelementptr i8, ptr %x, i16 %y
+  ret ptr %a
+}
+
+define ptr @ptradd_i32(ptr %x, i32 %y) {
+; WASM32-LABEL: ptradd_i32:
+; WASM32:         .functype ptradd_i32 (i32, i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push2=, 0
+; WASM32-NEXT:    local.get $push1=, 1
+; WASM32-NEXT:    i32.add $push0=, $pop2, $pop1
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: ptradd_i32:
+; WASM64:         .functype ptradd_i32 (i64, i32) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push3=, 0
+; WASM64-NEXT:    local.get $push2=, 1
+; WASM64-NEXT:    i64.extend_i32_s $push0=, $pop2
+; WASM64-NEXT:    i64.add $push1=, $pop3, $pop0
+; WASM64-NEXT:    return $pop1
+  %a = getelementptr i8, ptr %x, i32 %y
+  ret ptr %a
+}
+
+define ptr @ptradd_i64(ptr %x, i64 %y) {
+; WASM32-LABEL: ptradd_i64:
+; WASM32:         .functype ptradd_i64 (i32, i64) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push3=, 0
+; WASM32-NEXT:    local.get $push2=, 1
+; WASM32-NEXT:    i32.wrap_i64 $push0=, $pop2
+; WASM32-NEXT:    i32.add $push1=, $pop3, $pop0
+; WASM32-NEXT:    return $pop1
+;
+; WASM64-LABEL: ptradd_i64:
+; WASM64:         .functype ptradd_i64 (i64, i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push2=, 0
+; WASM64-NEXT:    local.get $push1=, 1
+; WASM64-NEXT:    i64.add $push0=, $pop2, $pop1
+; WASM64-NEXT:    return $pop0
+  %a = getelementptr i8, ptr %x, i64 %y
+  ret ptr %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask.ll
new file mode 100644
index 0000000000000..ab4e4de2ad79d
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: sed 's/iX/i32/g' < %s > %t && llc < %t -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %t
+; RUN: sed 's/iX/i64/g' < %s > %t && llc < %t -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %t
+
+declare ptr @llvm.ptrmask(ptr, iX)
+
+define ptr @ptrmask(ptr %x, iX %y) {
+; CHECK-LABEL: ptrmask:
+; CHECK:         .functype ptrmask (iX, iX) -> (iX)
+; CHECK-NEXT:  # %bb.0:
+; CHECK-NEXT:    local.get $push2=, 0
+; CHECK-NEXT:    local.get $push1=, 1
+; CHECK-NEXT:    iX.and $push0=, $pop2, $pop1
+; CHECK-NEXT:    return $pop0
+  %a = call ptr @llvm.ptrmask(ptr %x, iX %y)
+  ret ptr %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm32.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm32.mir
new file mode 100644
index 0000000000000..5ed664d6de902
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm32.mir
@@ -0,0 +1,85 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=CHECK
+
+---
+name:            ptrmask_i8
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i8
+    ; 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: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 255, implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I32_:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_1]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I32_1:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_]], [[AND_I32_]]
+    ; CHECK-NEXT: RETURN [[AND_I32_1]], implicit-def $arguments
+    %0:i32(p0)  = ARGUMENT_i32 0, implicit $arguments
+    %2:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(i8) = G_TRUNC %2(i32)
+    %3:_(p0) = G_PTRMASK %0, %1(i8)
+    RETURN %3(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i16
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i16
+    ; 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: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 65535, implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I32_:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_1]], [[CONST_I32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I32_1:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_]], [[AND_I32_]]
+    ; CHECK-NEXT: RETURN [[AND_I32_1]], implicit-def $arguments
+    %0:i32(p0)  = ARGUMENT_i32 0, implicit $arguments
+    %2:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(i16) = G_TRUNC %2(i32)
+    %3:_(p0) = G_PTRMASK %0, %1(i16)
+    RETURN %3(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i32
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_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]]
+    ; CHECK-NEXT: RETURN [[AND_I32_]], implicit-def $arguments
+    %0:i32(p0)  = ARGUMENT_i32 0, implicit $arguments
+    %1:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %2:_(p0) = G_PTRMASK %0, %1(i32)
+    RETURN %2(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i64
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i64
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 1, implicit $arguments
+    ; CHECK-NEXT: [[I32_WRAP_I64_:%[0-9]+]]:i32 = I32_WRAP_I64 [[ARGUMENT_i64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I32_:%[0-9]+]]:i32 = AND_I32 [[ARGUMENT_i32_]], [[I32_WRAP_I64_]]
+    ; CHECK-NEXT: RETURN [[AND_I32_]], implicit-def $arguments
+    %0:i32(p0)  = ARGUMENT_i32 0, implicit $arguments
+    %1:i64(i64) = ARGUMENT_i64 1, implicit $arguments
+    %2:_(p0) = G_PTRMASK %0, %1(i64)
+    RETURN %2(p0), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm64.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm64.mir
new file mode 100644
index 0000000000000..3106f7bd9670b
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrmask_wasm64.mir
@@ -0,0 +1,87 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=wasm64-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=CHECK
+
+---
+name:            ptrmask_i8
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i8
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[I64_EXTEND_U_I32_:%[0-9]+]]:i64 = I64_EXTEND_U_I32 [[ARGUMENT_i32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 255, implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I64_:%[0-9]+]]:i64 = AND_I64 [[I64_EXTEND_U_I32_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I64_1:%[0-9]+]]:i64 = AND_I64 [[ARGUMENT_i64_]], [[AND_I64_]]
+    ; CHECK-NEXT: RETURN [[AND_I64_1]], implicit-def $arguments
+    %0:i64(p0)  = ARGUMENT_i64 0, implicit $arguments
+    %2:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(i8) = G_TRUNC %2(i32)
+    %3:_(p0) = G_PTRMASK %0, %1(i8)
+    RETURN %3(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i16
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i16
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[I64_EXTEND_U_I32_:%[0-9]+]]:i64 = I64_EXTEND_U_I32 [[ARGUMENT_i32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 65535, implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I64_:%[0-9]+]]:i64 = AND_I64 [[I64_EXTEND_U_I32_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I64_1:%[0-9]+]]:i64 = AND_I64 [[ARGUMENT_i64_]], [[AND_I64_]]
+    ; CHECK-NEXT: RETURN [[AND_I64_1]], implicit-def $arguments
+    %0:i64(p0)  = ARGUMENT_i64 0, implicit $arguments
+    %2:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %1:_(i16) = G_TRUNC %2(i32)
+    %3:_(p0) = G_PTRMASK %0, %1(i16)
+    RETURN %3(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i32
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_i32
+    ; CHECK: liveins: $arguments
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[ARGUMENT_i64_:%[0-9]+]]:i64 = ARGUMENT_i64 0, implicit $arguments
+    ; CHECK-NEXT: [[ARGUMENT_i32_:%[0-9]+]]:i32 = ARGUMENT_i32 1, implicit $arguments
+    ; CHECK-NEXT: [[I64_EXTEND_U_I32_:%[0-9]+]]:i64 = I64_EXTEND_U_I32 [[ARGUMENT_i32_]], implicit-def dead $arguments
+    ; CHECK-NEXT: [[AND_I64_:%[0-9]+]]:i64 = AND_I64 [[ARGUMENT_i64_]], [[I64_EXTEND_U_I32_]]
+    ; CHECK-NEXT: RETURN [[AND_I64_]], implicit-def $arguments
+    %0:i64(p0)  = ARGUMENT_i64 0, implicit $arguments
+    %1:i32(i32) = ARGUMENT_i32 1, implicit $arguments
+    %2:_(p0) = G_PTRMASK %0, %1(i32)
+    RETURN %2(p0), implicit-def $arguments
+...
+
+---
+name:            ptrmask_i64
+tracksRegLiveness: true
+body:             |
+  bb.1.entry:
+    liveins: $arguments
+    ; CHECK-LABEL: name: ptrmask_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]]
+    ; CHECK-NEXT: RETURN [[AND_I64_]], implicit-def $arguments
+    %0:i64(p0)  = ARGUMENT_i64 0, implicit $arguments
+    %1:i64(i64) = ARGUMENT_i64 1, implicit $arguments
+    %2:_(p0) = G_PTRMASK %0, %1(i64)
+    RETURN %2(p0), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrtoint.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrtoint.ll
new file mode 100644
index 0000000000000..fe90bfe260f75
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/ptrtoint.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define i8 @ptrtoint_i8(ptr %x) {
+; WASM32-LABEL: ptrtoint_i8:
+; WASM32:         .functype ptrtoint_i8 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push0=, 0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: ptrtoint_i8:
+; WASM64:         .functype ptrtoint_i8 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.wrap_i64 $push0=, $pop1
+; WASM64-NEXT:    return $pop0
+  %a = ptrtoint ptr %x to i8
+  ret i8 %a
+}
+
+define i16 @ptrtoint_i16(ptr %x) {
+; WASM32-LABEL: ptrtoint_i16:
+; WASM32:         .functype ptrtoint_i16 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push0=, 0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: ptrtoint_i16:
+; WASM64:         .functype ptrtoint_i16 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.wrap_i64 $push0=, $pop1
+; WASM64-NEXT:    return $pop0
+  %a = ptrtoint ptr %x to i16
+  ret i16 %a
+}
+
+define i32 @ptrtoint_i32(ptr %x) {
+; WASM32-LABEL: ptrtoint_i32:
+; WASM32:         .functype ptrtoint_i32 (i32) -> (i32)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push0=, 0
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: ptrtoint_i32:
+; WASM64:         .functype ptrtoint_i32 (i64) -> (i32)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    i32.wrap_i64 $push0=, $pop1
+; WASM64-NEXT:    return $pop0
+  %a = ptrtoint ptr %x to i32
+  ret i32 %a
+}
+
+define i64 @ptrtoint_i64(ptr %x) {
+; WASM32-LABEL: ptrtoint_i64:
+; WASM32:         .functype ptrtoint_i64 (i32) -> (i64)
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    i64.extend_i32_u $push0=, $pop1
+; WASM32-NEXT:    return $pop0
+;
+; WASM64-LABEL: ptrtoint_i64:
+; WASM64:         .functype ptrtoint_i64 (i64) -> (i64)
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push0=, 0
+; WASM64-NEXT:    return $pop0
+  %a = ptrtoint ptr %x to i64
+  ret i64 %a
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/sextload.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/sextload.mir
new file mode 100644
index 0000000000000..273f501048f2a
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/sextload.mir
@@ -0,0 +1,220 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=WASM32
+# RUN: llc -mtriple=wasm64-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=WASM64
+
+---
+name: sextload_i1_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i1_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I32_A32_:%[0-9]+]]:i32 = LOAD8_U_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 31, implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHL_I32_:%[0-9]+]]:i32 = SHL_I32 [[LOAD8_U_I32_A32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHR_S_I32_:%[0-9]+]]:i32 = SHR_S_I32 [[SHL_I32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: RETURN [[SHR_S_I32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i1_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I32_A64_:%[0-9]+]]:i32 = LOAD8_U_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 31, implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHL_I32_:%[0-9]+]]:i32 = SHL_I32 [[LOAD8_U_I32_A64_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHR_S_I32_:%[0-9]+]]:i32 = SHR_S_I32 [[SHL_I32_]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM64-NEXT: RETURN [[SHR_S_I32_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_SEXTLOAD %0(p0) :: (load (i1))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: sextload_i1_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i1_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I64_A32_:%[0-9]+]]:i64 = LOAD8_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 63, implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD8_U_I64_A32_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHR_S_I64_:%[0-9]+]]:i64 = SHR_S_I64 [[SHL_I64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM32-NEXT: RETURN [[SHR_S_I64_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i1_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I64_A64_:%[0-9]+]]:i64 = LOAD8_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 63, implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD8_U_I64_A64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHR_S_I64_:%[0-9]+]]:i64 = SHR_S_I64 [[SHL_I64_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: RETURN [[SHR_S_I64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_SEXTLOAD %0(p0) :: (load (i1))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: sextload_i8_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i8_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_S_I32_A32_:%[0-9]+]]:i32 = LOAD8_S_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_S_I32_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i8_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_S_I32_A64_:%[0-9]+]]:i32 = LOAD8_S_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_S_I32_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_SEXTLOAD %0(p0) :: (load (i8))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: sextload_i8_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i8_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_S_I64_A32_:%[0-9]+]]:i64 = LOAD8_S_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_S_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i8_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_S_I64_A64_:%[0-9]+]]:i64 = LOAD8_S_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_S_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_SEXTLOAD %0(p0) :: (load (i8))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: sextload_i16_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i16_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD16_S_I32_A32_:%[0-9]+]]:i32 = LOAD16_S_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM32-NEXT: RETURN [[LOAD16_S_I32_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i16_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD16_S_I32_A64_:%[0-9]+]]:i32 = LOAD16_S_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM64-NEXT: RETURN [[LOAD16_S_I32_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_SEXTLOAD %0(p0) :: (load (i16))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: sextload_i16_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i16_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD16_S_I64_A32_:%[0-9]+]]:i64 = LOAD16_S_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM32-NEXT: RETURN [[LOAD16_S_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i16_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD16_S_I64_A64_:%[0-9]+]]:i64 = LOAD16_S_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM64-NEXT: RETURN [[LOAD16_S_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_SEXTLOAD %0(p0) :: (load (i16))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: sextload_i32_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i32_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD32_S_I64_A32_:%[0-9]+]]:i64 = LOAD32_S_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i32))
+    ; WASM32-NEXT: RETURN [[LOAD32_S_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i32_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD32_S_I64_A64_:%[0-9]+]]:i64 = LOAD32_S_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i32))
+    ; WASM64-NEXT: RETURN [[LOAD32_S_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_SEXTLOAD %0(p0) :: (load (i32))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: sextload_i48_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: sextload_i48_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD32_U_I64_A32_:%[0-9]+]]:i64 = LOAD32_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (s32), align 8)
+    ; WASM32-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 4, implicit-def dead $arguments
+    ; WASM32-NEXT: [[ADD_I32_:%[0-9]+]]:i32 = nuw inbounds ADD_I32 [[DEF]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[LOAD16_S_I64_A32_:%[0-9]+]]:i64 = LOAD16_S_I64_A32 0, 0, [[ADD_I32_]], implicit-def dead $arguments :: (load (s16) from unknown-address + 4, align 4)
+    ; WASM32-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 32, implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD16_S_I64_A32_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[OR_I64_:%[0-9]+]]:i64 = OR_I64 [[SHL_I64_]], [[LOAD32_U_I64_A32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: RETURN [[OR_I64_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: sextload_i48_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD32_U_I64_A64_:%[0-9]+]]:i64 = LOAD32_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (s32), align 8)
+    ; WASM64-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 4, implicit-def dead $arguments
+    ; WASM64-NEXT: [[ADD_I64_:%[0-9]+]]:i64 = nuw inbounds ADD_I64 [[DEF]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[LOAD16_S_I64_A64_:%[0-9]+]]:i64 = LOAD16_S_I64_A64 0, 0, [[ADD_I64_]], implicit-def dead $arguments :: (load (s16) from unknown-address + 4, align 4)
+    ; WASM64-NEXT: [[CONST_I64_1:%[0-9]+]]:i64 = CONST_I64 32, implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD16_S_I64_A64_]], [[CONST_I64_1]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[OR_I64_:%[0-9]+]]:i64 = OR_I64 [[SHL_I64_]], [[LOAD32_U_I64_A64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: RETURN [[OR_I64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_SEXTLOAD %0(p0) :: (load (i48))
+    RETURN %1(i64), implicit-def $arguments
+...
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/store.ll b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/store.ll
new file mode 100644
index 0000000000000..2bfc59dfe37ee
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/store.ll
@@ -0,0 +1,236 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -O0 -mtriple=wasm32-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM32
+; RUN: llc < %s -O0 -mtriple=wasm64-unknown-unknown --global-isel -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s --check-prefix=WASM64
+
+define void @store_i1(ptr %x, i1 %y) {
+; WASM32-LABEL: store_i1:
+; WASM32:         .functype store_i1 (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push5=, 0
+; WASM32-NEXT:    local.get $push4=, 1
+; WASM32-NEXT:    i32.const $push3=, 1
+; WASM32-NEXT:    i32.and $push0=, $pop4, $pop3
+; WASM32-NEXT:    i32.const $push1=, 1
+; WASM32-NEXT:    i32.and $push2=, $pop0, $pop1
+; WASM32-NEXT:    i32.store8 0($pop5), $pop2
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i1:
+; WASM64:         .functype store_i1 (i64, i32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push5=, 0
+; WASM64-NEXT:    local.get $push4=, 1
+; WASM64-NEXT:    i32.const $push3=, 1
+; WASM64-NEXT:    i32.and $push0=, $pop4, $pop3
+; WASM64-NEXT:    i32.const $push1=, 1
+; WASM64-NEXT:    i32.and $push2=, $pop0, $pop1
+; WASM64-NEXT:    i32.store8 0($pop5), $pop2
+; WASM64-NEXT:    return
+  store i1 %y, ptr %x
+  ret void
+}
+
+define void @store_i8(ptr %x, i8 %y) {
+; WASM32-LABEL: store_i8:
+; WASM32:         .functype store_i8 (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store8 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i8:
+; WASM64:         .functype store_i8 (i64, i32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i32.store8 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store i8 %y, ptr %x
+  ret void
+}
+
+define void @store_i16(ptr %x, i16 %y) {
+; WASM32-LABEL: store_i16:
+; WASM32:         .functype store_i16 (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store16 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i16:
+; WASM64:         .functype store_i16 (i64, i32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i32.store16 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store i16 %y, ptr %x
+  ret void
+}
+
+define void @store_i32(ptr %x, i32 %y) {
+; WASM32-LABEL: store_i32:
+; WASM32:         .functype store_i32 (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i32:
+; WASM64:         .functype store_i32 (i64, i32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i32.store 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store i32 %y, ptr %x
+  ret void
+}
+
+define void @store_i32_align1(ptr %x, i32 %y) {
+; WASM32-LABEL: store_i32_align1:
+; WASM32:         .functype store_i32_align1 (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store 0($pop1):p2align=0, $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i32_align1:
+; WASM64:         .functype store_i32_align1 (i64, i32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i32.store 0($pop1):p2align=0, $pop0
+; WASM64-NEXT:    return
+  store i32 %y, ptr %x, align 1
+  ret void
+}
+
+define void @store_i48(ptr %x, i48 %y) {
+; WASM32-LABEL: store_i48:
+; WASM32:         .functype store_i48 (i32, i64) -> ()
+; WASM32-NEXT:    .local i64, i32
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push3=, 1
+; WASM32-NEXT:    i64.const $push0=, 32
+; WASM32-NEXT:    i64.shr_u $push2=, $pop3, $pop0
+; WASM32-NEXT:    local.set 2, $pop2
+; WASM32-NEXT:    local.get $push5=, 0
+; WASM32-NEXT:    i32.const $push1=, 4
+; WASM32-NEXT:    i32.add $push4=, $pop5, $pop1
+; WASM32-NEXT:    local.set 3, $pop4
+; WASM32-NEXT:    local.get $push7=, 0
+; WASM32-NEXT:    local.get $push6=, 1
+; WASM32-NEXT:    i64.store32 0($pop7), $pop6
+; WASM32-NEXT:    local.get $push9=, 3
+; WASM32-NEXT:    local.get $push8=, 2
+; WASM32-NEXT:    i64.store16 0($pop9), $pop8
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i48:
+; WASM64:         .functype store_i48 (i64, i64) -> ()
+; WASM64-NEXT:    .local i64, i64
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push3=, 1
+; WASM64-NEXT:    i64.const $push0=, 32
+; WASM64-NEXT:    i64.shr_u $push2=, $pop3, $pop0
+; WASM64-NEXT:    local.set 2, $pop2
+; WASM64-NEXT:    local.get $push5=, 0
+; WASM64-NEXT:    i64.const $push1=, 4
+; WASM64-NEXT:    i64.add $push4=, $pop5, $pop1
+; WASM64-NEXT:    local.set 3, $pop4
+; WASM64-NEXT:    local.get $push7=, 0
+; WASM64-NEXT:    local.get $push6=, 1
+; WASM64-NEXT:    i64.store32 0($pop7), $pop6
+; WASM64-NEXT:    local.get $push9=, 3
+; WASM64-NEXT:    local.get $push8=, 2
+; WASM64-NEXT:    i64.store16 0($pop9), $pop8
+; WASM64-NEXT:    return
+  store i48 %y, ptr %x
+  ret void
+}
+
+define void @store_i64(ptr %x, i64 %y) {
+; WASM32-LABEL: store_i64:
+; WASM32:         .functype store_i64 (i32, i64) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i64.store 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_i64:
+; WASM64:         .functype store_i64 (i64, i64) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i64.store 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store i64 %y, ptr %x
+  ret void
+}
+
+
+define void @store_f32(ptr %x, float %y) {
+; WASM32-LABEL: store_f32:
+; WASM32:         .functype store_f32 (i32, f32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_f32:
+; WASM64:         .functype store_f32 (i64, f32) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i32.store 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store float %y, ptr %x
+  ret void
+}
+
+define void @store_f64(ptr %x, double %y) {
+; WASM32-LABEL: store_f64:
+; WASM32:         .functype store_f64 (i32, f64) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i64.store 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_f64:
+; WASM64:         .functype store_f64 (i64, f64) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i64.store 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store double %y, ptr %x
+  ret void
+}
+
+define void @store_ptr(ptr %x, ptr %y) {
+; WASM32-LABEL: store_ptr:
+; WASM32:         .functype store_ptr (i32, i32) -> ()
+; WASM32-NEXT:  # %bb.0:
+; WASM32-NEXT:    local.get $push1=, 0
+; WASM32-NEXT:    local.get $push0=, 1
+; WASM32-NEXT:    i32.store 0($pop1), $pop0
+; WASM32-NEXT:    return
+;
+; WASM64-LABEL: store_ptr:
+; WASM64:         .functype store_ptr (i64, i64) -> ()
+; WASM64-NEXT:  # %bb.0:
+; WASM64-NEXT:    local.get $push1=, 0
+; WASM64-NEXT:    local.get $push0=, 1
+; WASM64-NEXT:    i64.store 0($pop1), $pop0
+; WASM64-NEXT:    return
+  store ptr %y, ptr %x
+  ret void
+}
diff --git a/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/zextload.mir b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/zextload.mir
new file mode 100644
index 0000000000000..6bf9ba7230121
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/GlobalISel/instructions/zextload.mir
@@ -0,0 +1,208 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=wasm32-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=WASM32
+# RUN: llc -mtriple=wasm64-unknown-unknown -run-pass=legalizer,regbankselect,instruction-select %s -o - | FileCheck %s --check-prefixes=WASM64
+
+---
+name: zextload_i1_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i1_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I32_A32_:%[0-9]+]]:i32 = LOAD8_U_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_U_I32_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i1_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I32_A64_:%[0-9]+]]:i32 = LOAD8_U_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_U_I32_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_ZEXTLOAD %0(p0) :: (load (i1))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: zextload_i1_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i1_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I64_A32_:%[0-9]+]]:i64 = LOAD8_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_U_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i1_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I64_A64_:%[0-9]+]]:i64 = LOAD8_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_U_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_ZEXTLOAD %0(p0) :: (load (i1))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: zextload_i8_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i8_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I32_A32_:%[0-9]+]]:i32 = LOAD8_U_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_U_I32_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i8_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I32_A64_:%[0-9]+]]:i32 = LOAD8_U_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_U_I32_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_ZEXTLOAD %0(p0) :: (load (i8))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: zextload_i8_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i8_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD8_U_I64_A32_:%[0-9]+]]:i64 = LOAD8_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM32-NEXT: RETURN [[LOAD8_U_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i8_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD8_U_I64_A64_:%[0-9]+]]:i64 = LOAD8_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i8))
+    ; WASM64-NEXT: RETURN [[LOAD8_U_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_ZEXTLOAD %0(p0) :: (load (i8))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: zextload_i16_i32
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i16_i32
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD16_U_I32_A32_:%[0-9]+]]:i32 = LOAD16_U_I32_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM32-NEXT: RETURN [[LOAD16_U_I32_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i16_i32
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD16_U_I32_A64_:%[0-9]+]]:i32 = LOAD16_U_I32_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM64-NEXT: RETURN [[LOAD16_U_I32_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i32) = G_ZEXTLOAD %0(p0) :: (load (i16))
+    RETURN %1(i32), implicit-def $arguments
+...
+---
+name: zextload_i16_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i16_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD16_U_I64_A32_:%[0-9]+]]:i64 = LOAD16_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM32-NEXT: RETURN [[LOAD16_U_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i16_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD16_U_I64_A64_:%[0-9]+]]:i64 = LOAD16_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i16))
+    ; WASM64-NEXT: RETURN [[LOAD16_U_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_ZEXTLOAD %0(p0) :: (load (i16))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: zextload_i32_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i32_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD32_U_I64_A32_:%[0-9]+]]:i64 = LOAD32_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i32))
+    ; WASM32-NEXT: RETURN [[LOAD32_U_I64_A32_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i32_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD32_U_I64_A64_:%[0-9]+]]:i64 = LOAD32_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (i32))
+    ; WASM64-NEXT: RETURN [[LOAD32_U_I64_A64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_ZEXTLOAD %0(p0) :: (load (i32))
+    RETURN %1(i64), implicit-def $arguments
+...
+---
+name: zextload_i48_i64
+tracksRegLiveness: true
+body: |
+  bb.1.entry:
+    liveins: $arguments
+
+    ; WASM32-LABEL: name: zextload_i48_i64
+    ; WASM32: liveins: $arguments
+    ; WASM32-NEXT: {{  $}}
+    ; WASM32-NEXT: [[DEF:%[0-9]+]]:i32 = IMPLICIT_DEF
+    ; WASM32-NEXT: [[LOAD32_U_I64_A32_:%[0-9]+]]:i64 = LOAD32_U_I64_A32 0, 0, [[DEF]], implicit-def dead $arguments :: (load (s32), align 8)
+    ; WASM32-NEXT: [[CONST_I32_:%[0-9]+]]:i32 = CONST_I32 4, implicit-def dead $arguments
+    ; WASM32-NEXT: [[ADD_I32_:%[0-9]+]]:i32 = nuw inbounds ADD_I32 [[DEF]], [[CONST_I32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[LOAD16_U_I64_A32_:%[0-9]+]]:i64 = LOAD16_U_I64_A32 0, 0, [[ADD_I32_]], implicit-def dead $arguments :: (load (s16) from unknown-address + 4, align 4)
+    ; WASM32-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 32, implicit-def dead $arguments
+    ; WASM32-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD16_U_I64_A32_]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM32-NEXT: [[OR_I64_:%[0-9]+]]:i64 = OR_I64 [[SHL_I64_]], [[LOAD32_U_I64_A32_]], implicit-def dead $arguments
+    ; WASM32-NEXT: RETURN [[OR_I64_]], implicit-def $arguments
+    ;
+    ; WASM64-LABEL: name: zextload_i48_i64
+    ; WASM64: liveins: $arguments
+    ; WASM64-NEXT: {{  $}}
+    ; WASM64-NEXT: [[DEF:%[0-9]+]]:i64 = IMPLICIT_DEF
+    ; WASM64-NEXT: [[LOAD32_U_I64_A64_:%[0-9]+]]:i64 = LOAD32_U_I64_A64 0, 0, [[DEF]], implicit-def dead $arguments :: (load (s32), align 8)
+    ; WASM64-NEXT: [[CONST_I64_:%[0-9]+]]:i64 = CONST_I64 4, implicit-def dead $arguments
+    ; WASM64-NEXT: [[ADD_I64_:%[0-9]+]]:i64 = nuw inbounds ADD_I64 [[DEF]], [[CONST_I64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[LOAD16_U_I64_A64_:%[0-9]+]]:i64 = LOAD16_U_I64_A64 0, 0, [[ADD_I64_]], implicit-def dead $arguments :: (load (s16) from unknown-address + 4, align 4)
+    ; WASM64-NEXT: [[CONST_I64_1:%[0-9]+]]:i64 = CONST_I64 32, implicit-def dead $arguments
+    ; WASM64-NEXT: [[SHL_I64_:%[0-9]+]]:i64 = SHL_I64 [[LOAD16_U_I64_A64_]], [[CONST_I64_1]], implicit-def dead $arguments
+    ; WASM64-NEXT: [[OR_I64_:%[0-9]+]]:i64 = OR_I64 [[SHL_I64_]], [[LOAD32_U_I64_A64_]], implicit-def dead $arguments
+    ; WASM64-NEXT: RETURN [[OR_I64_]], implicit-def $arguments
+    %0:_(p0) = G_IMPLICIT_DEF
+    %1:_(i64) = G_ZEXTLOAD %0(p0) :: (load (i48))
+    RETURN %1(i64), implicit-def $arguments
+...

>From 2399bfd8474cf5b867206217307c8ec845942dc7 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Wed, 1 Jul 2026 11:32:16 -0700
Subject: [PATCH 2/3] InstructionSelector tweaks after review

---
 .../GISel/WebAssemblyInstructionSelector.cpp  | 42 +++++++------------
 1 file changed, 14 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 3709327170ac6..6fdbe38ace1ab 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -99,10 +99,6 @@ InstructionSelector::ComplexRendererFns
 WebAssemblyInstructionSelector::selectAddrOperands(LLT AddrType,
                                                    unsigned int ConstOpc,
                                                    MachineOperand &Root) const {
-
-  if (!Root.isReg())
-    return std::nullopt;
-
   return {{
       [=](MachineInstrBuilder &MIB) { MIB.addImm(0); },
       [=](MachineInstrBuilder &MIB) { MIB.addReg(Root.getReg()); },
@@ -190,9 +186,6 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
 
   using namespace TargetOpcode;
 
-  auto PointerWidth = MF.getDataLayout().getPointerSizeInBits();
-  auto PtrIsI64 = PointerWidth == 64;
-
   switch (I.getOpcode()) {
   case G_IMPLICIT_DEF: {
     const Register DefReg = I.getOperand(0).getReg();
@@ -207,43 +200,36 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
     return RBI.constrainGenericRegister(DefReg, *DefRC, MRI) != nullptr;
   }
   case G_PTRTOINT: {
-    assert(MRI.getType(I.getOperand(1).getReg()).isPointer() &&
-           "G_PTRTOINT selection with non-pointer?");
+    bool PtrIsI64 = MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == 64;
 
     I.setDesc(
         TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
-
     return true;
   }
   case G_INTTOPTR: {
-    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
-           "G_INTTOPTR selection with non-pointer?");
+    bool PtrIsI64 = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() == 64;
 
     I.setDesc(
         TII.get(PtrIsI64 ? WebAssembly::COPY_I64 : WebAssembly::COPY_I32));
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
-
     return true;
   }
   case G_PTRMASK: {
-    assert(MRI.getType(I.getOperand(0).getReg()).isPointer() &&
-           "G_PTRMASK selection with non-pointer?");
+    bool PtrIsI64 = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() == 64;
 
     I.setDesc(TII.get(PtrIsI64 ? WebAssembly::AND_I64 : WebAssembly::AND_I32));
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
-
     return true;
   }
   case G_GLOBAL_VALUE: {
     assert(I.getOperand(1).getTargetFlags() == 0 &&
            "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
-    assert(WebAssembly::isValidAddressSpace(
-               MRI.getType(I.getOperand(0).getReg()).getAddressSpace()) &&
-           "Invalid address space for WebAssembly target");
 
     unsigned OperandFlags = 0;
     const llvm::GlobalValue *GV = I.getOperand(1).getGlobal();
+    LLT PtrTy = MRI.getType(I.getOperand(0).getReg());
+    bool PtrIsI64 = PtrTy.getSizeInBits() == 64;
 
     if (TLI.isPositionIndependent()) {
       if (TM.shouldAssumeDSOLocal(GV)) {
@@ -257,14 +243,13 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
         }
         MachineIRBuilder B(I);
 
-        auto MemBase =
-            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
-        MRI.setRegClass(MemBase, PtrIsI64 ? &WebAssembly::I64RegClass
-                                          : &WebAssembly::I32RegClass);
-        auto Offset =
-            MRI.createGenericVirtualRegister(LLT::pointer(0, PointerWidth));
-        MRI.setRegClass(Offset, PtrIsI64 ? &WebAssembly::I64RegClass
-                                         : &WebAssembly::I32RegClass);
+        Register MemBase = MRI.createVirtualRegister(
+            PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
+        MRI.setType(MemBase, PtrTy);
+
+        Register Offset = MRI.createVirtualRegister(
+            PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
+        MRI.setType(Offset, PtrTy);
 
         B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
                               : WebAssembly::GLOBAL_GET_I32)
@@ -288,7 +273,8 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
       OperandFlags = WebAssemblyII::MO_GOT;
     }
 
-    auto NewOpc = PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
+    unsigned NewOpc =
+        PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
 
     if (OperandFlags & WebAssemblyII::MO_GOT) {
       NewOpc =

>From ebff1554446295115758aff11cc8be3e97bb2020 Mon Sep 17 00:00:00 2001
From: Demetrius Kanios <demetrius at kanios.net>
Date: Tue, 4 Aug 2026 01:04:55 -0700
Subject: [PATCH 3/3] Try moving G_GLOBAL_VALUE handling to legalizer

---
 .../GISel/WebAssemblyInstructionSelector.cpp  | 86 ++++++-------------
 .../GISel/WebAssemblyLegalizerInfo.cpp        | 65 +++++++++++++-
 .../WebAssembly/WebAssemblyInstrGISel.td      | 35 ++++++++
 .../WebAssembly/WebAssemblyInstrInfo.td       |  1 +
 4 files changed, 128 insertions(+), 59 deletions(-)
 create mode 100644 llvm/lib/Target/WebAssembly/WebAssemblyInstrGISel.td

diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
index 6fdbe38ace1ab..23413db8fd9c7 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyInstructionSelector.cpp
@@ -222,69 +222,39 @@ bool WebAssemblyInstructionSelector::select(MachineInstr &I) {
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
     return true;
   }
-  case G_GLOBAL_VALUE: {
-    assert(I.getOperand(1).getTargetFlags() == 0 &&
-           "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
-
-    unsigned OperandFlags = 0;
-    const llvm::GlobalValue *GV = I.getOperand(1).getGlobal();
-    LLT PtrTy = MRI.getType(I.getOperand(0).getReg());
-    bool PtrIsI64 = PtrTy.getSizeInBits() == 64;
-
-    if (TLI.isPositionIndependent()) {
-      if (TM.shouldAssumeDSOLocal(GV)) {
-        const char *BaseName;
-        if (GV->getValueType()->isFunctionTy()) {
-          BaseName = MF.createExternalSymbolName("__table_base");
-          OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL;
-        } else {
-          BaseName = MF.createExternalSymbolName("__memory_base");
-          OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL;
-        }
-        MachineIRBuilder B(I);
-
-        Register MemBase = MRI.createVirtualRegister(
-            PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
-        MRI.setType(MemBase, PtrTy);
-
-        Register Offset = MRI.createVirtualRegister(
-            PtrIsI64 ? &WebAssembly::I64RegClass : &WebAssembly::I32RegClass);
-        MRI.setType(Offset, PtrTy);
-
-        B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
-                              : WebAssembly::GLOBAL_GET_I32)
-            .addDef(MemBase)
-            .addExternalSymbol(BaseName);
-
-        B.buildInstr(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32)
-            .addDef(Offset)
-            .addGlobalAddress(GV, I.getOperand(1).getOffset(), OperandFlags);
-
-        auto MIB =
-            B.buildInstr(PtrIsI64 ? WebAssembly::ADD_I64 : WebAssembly::ADD_I32)
-                .addDef(I.getOperand(0).getReg())
-                .addReg(MemBase)
-                .addReg(Offset);
-        constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI);
-
-        I.eraseFromParent();
-        return true;
-      }
-      OperandFlags = WebAssemblyII::MO_GOT;
-    }
+  case WebAssembly::G_Wrapper: {
+    LLT DefTy = MRI.getType(I.getOperand(0).getReg());
 
-    unsigned NewOpc =
-        PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32;
+    if (!DefTy.isPointer() || DefTy.getAddressSpace() != 0)
+      return false;
 
-    if (OperandFlags & WebAssemblyII::MO_GOT) {
-      NewOpc =
-          PtrIsI64 ? WebAssembly::GLOBAL_GET_I64 : WebAssembly::GLOBAL_GET_I32;
-    }
+    bool PtrIsI64 = DefTy.getSizeInBits() == 64;
+
+    if (TLI.isPositionIndependent())
+      I.setDesc(TII.get(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
+                                 : WebAssembly::GLOBAL_GET_I32));
+    else
+      I.setDesc(
+          TII.get(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32));
 
-    I.setDesc(TII.get(NewOpc));
-    I.getOperand(1).setTargetFlags(OperandFlags);
     constrainSelectedInstRegOperands(I, TII, TRI, RBI);
+    return true;
+  }
+  case WebAssembly::G_WrapperREL: {
+    LLT DefTy = MRI.getType(I.getOperand(0).getReg());
 
+    if (!DefTy.isInteger())
+      return false;
+
+    bool PtrIsI64 = DefTy.getSizeInBits() == 64;
+
+    if (TLI.isPositionIndependent())
+      I.setDesc(
+          TII.get(PtrIsI64 ? WebAssembly::CONST_I64 : WebAssembly::CONST_I32));
+    else
+      return false;
+
+    constrainSelectedInstRegOperands(I, TII, TRI, RBI);
     return true;
   }
   default:
diff --git a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
index 8ceebac8c4c72..c50ef8f1aab64 100644
--- a/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/GISel/WebAssemblyLegalizerInfo.cpp
@@ -15,6 +15,11 @@
 #include "WebAssemblySubtarget.h"
 #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
 #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/Target/TargetMachine.h"
 
 #define DEBUG_TYPE "wasm-legalinfo"
 
@@ -169,7 +174,8 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
       .legalFor({{p0, p0i}})
       .clampScalar(1, p0s, p0s);
 
-  getActionDefinitionsBuilder({G_FRAME_INDEX, G_GLOBAL_VALUE}).legalFor({p0});
+  getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({p0});
+  getActionDefinitionsBuilder(G_GLOBAL_VALUE).customFor({p0});
 
   getActionDefinitionsBuilder(G_LOAD)
       .legalForTypesWithMemDesc({{i32, p0, i8, 1},
@@ -219,6 +225,13 @@ WebAssemblyLegalizerInfo::WebAssemblyLegalizerInfo(
 bool WebAssemblyLegalizerInfo::legalizeCustom(
     LegalizerHelper &Helper, MachineInstr &MI,
     LostDebugLocObserver &LocObserver) const {
+  MachineBasicBlock &MBB = *MI.getParent();
+  MachineFunction &MF = *MBB.getParent();
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  const TargetMachine &TM = MF.getTarget();
+  const TargetLowering &TLI = Helper.getTargetLowering();
+  const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
+
   switch (MI.getOpcode()) {
   case TargetOpcode::G_SEXT_INREG: {
     assert(MI.getOperand(2).isImm() && "Expected immediate");
@@ -234,6 +247,56 @@ bool WebAssemblyLegalizerInfo::legalizeCustom(
 
     return Helper.lower(MI, 0, DstType) != LegalizerHelper::UnableToLegalize;
   }
+  case TargetOpcode::G_GLOBAL_VALUE: {
+    assert(MI.getOperand(1).getTargetFlags() == 0 &&
+           "Unexpected target flags on generic G_GLOBAL_VALUE instruction");
+
+    unsigned OperandFlags = 0;
+    const llvm::GlobalValue *GV = MI.getOperand(1).getGlobal();
+    LLT PtrTy = MRI.getType(MI.getOperand(0).getReg());
+    bool PtrIsI64 = PtrTy.getSizeInBits() == 64;
+
+    if (TLI.isPositionIndependent()) {
+      if (TM.shouldAssumeDSOLocal(GV)) {
+        MachineIRBuilder B(MI);
+
+        const char *BaseName;
+        if (GV->getValueType()->isFunctionTy()) {
+          BaseName = MF.createExternalSymbolName("__table_base");
+          OperandFlags = WebAssemblyII::MO_TABLE_BASE_REL;
+        } else {
+          BaseName = MF.createExternalSymbolName("__memory_base");
+          OperandFlags = WebAssemblyII::MO_MEMORY_BASE_REL;
+        }
+
+        Register BaseAddr = MRI.createGenericVirtualRegister(PtrTy);
+        B.buildInstr(PtrIsI64 ? WebAssembly::GLOBAL_GET_I64
+                              : WebAssembly::GLOBAL_GET_I32)
+            .addDef(BaseAddr)
+            .addExternalSymbol(BaseName);
+
+        Register SymAddr = MRI.createGenericVirtualRegister(
+            LLT::integer(PtrTy.getSizeInBits()));
+        B.buildInstr(WebAssembly::G_WrapperREL)
+            .addDef(SymAddr)
+            .addGlobalAddress(GV, MI.getOperand(1).getOffset(), OperandFlags);
+
+        B.buildInstr(TargetOpcode::G_PTR_ADD)
+            .addDef(MI.getOperand(0).getReg())
+            .addReg(BaseAddr)
+            .addReg(SymAddr);
+
+        MI.eraseFromParent();
+        return true;
+      }
+      OperandFlags = WebAssemblyII::MO_GOT;
+    }
+
+    MI.setDesc(TII.get(WebAssembly::G_Wrapper));
+    MI.getOperand(1).setTargetFlags(OperandFlags);
+
+    return true;
+  }
   default:
     break;
   }
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrGISel.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrGISel.td
new file mode 100644
index 0000000000000..5886dc8c73719
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrGISel.td
@@ -0,0 +1,35 @@
+//===-- WebAssemblyInstrGISel.td - Wasm GISel target pseudos -*- tablegen -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// WebAssembly GlobalISel target pseudo instruction definitions. This is kept
+// separately from the other tablegen files for organizational purposes, but
+// share the same infrastructure.
+//
+//===----------------------------------------------------------------------===//
+
+
+class WebAssemblyGenericInstruction : GenericInstruction {
+  let Namespace = "WebAssembly";
+}
+
+// non-standard casing to match the same for ISelDAG
+def G_Wrapper : WebAssemblyGenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type0:$src);
+  let hasSideEffects = false;
+}
+
+// non-standard casing to match the same for ISelDAG
+def G_WrapperREL : WebAssemblyGenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type0:$src);
+  let hasSideEffects = false;
+}
+
+def : GINodeEquiv<G_Wrapper, WebAssemblyWrapper>;
+def : GINodeEquiv<G_WrapperREL, WebAssemblyWrapperREL>;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 7669f301b2c48..5029414552747 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -483,3 +483,4 @@ include "WebAssemblyInstrSIMD.td"
 include "WebAssemblyInstrRef.td"
 include "WebAssemblyInstrBulkMemory.td"
 include "WebAssemblyInstrTable.td"
+include "WebAssemblyInstrGISel.td"



More information about the llvm-commits mailing list