[llvm] [WebAssembly] Avoid folding wasm_var loads through select (PR #214937)

Gauarv Chaudhary via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 8 00:08:44 PDT 2026


https://github.com/ANAMASGARD created https://github.com/llvm/llvm-project/pull/214937

 DAGCombiner can fold a select of two loads from WebAssembly globals into a load through a selected address. This is
  invalid for `wasm_var`, because WebAssembly globals are accessed by name with `global.get` and do not support computed
  addresses.

  Add a target legality hook and disable this fold only for WebAssembly `wasm_var` accesses. The fold remains enabled for
  other targets and for WebAssembly linear memory.



  Refs #206650.

  Related to #214811, which independently fixes reference-type `select` encoding.

  Tests:

  - llvm-lit -sv -j4 llvm/test/CodeGen/WebAssembly — 412/412 passed
  - global-select-load.ll and global-get-unlowerable.ll
  - Manual wasm_var and linear-memory checks
  - Combined verification with #214811:
      - local and global externref selects at -O0 and -O2 emit 1c 01 6f
      - the global externref case no longer crashes at -O2
      - the branch-form case compiles at -O2
      - explicit computed wasm_var addresses remain rejected


>From 4c904cb42cae7cd63663baedb1d0d5f10de54f2f Mon Sep 17 00:00:00 2001
From: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
Date: Sat, 8 Aug 2026 12:34:40 +0530
Subject: [PATCH] DAGCombiner can fold a select of two loads into a load
 through a select of their addresses. This is invalid for wasm_var because
 WebAssembly globals are accessed by identity with global.get, not through
 computed addresses.

  Add a TargetLowering legality hook and reject this fold only for wasm_var. The generic fold remains enabled for other
  targets and for WebAssembly linear memory.

  Refs #206650
  Related to #214811

Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
---
 llvm/include/llvm/CodeGen/TargetLowering.h    |  6 ++++
 llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp |  8 +++--
 .../WebAssembly/WebAssemblyISelLowering.cpp   |  5 ++++
 .../WebAssembly/WebAssemblyISelLowering.h     |  1 +
 .../CodeGen/WebAssembly/global-select-load.ll | 30 +++++++++++++++++++
 5 files changed, 47 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/WebAssembly/global-select-load.ll

diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 9a525d69b3ee8..681de782250c7 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -551,6 +551,12 @@ class LLVM_ABI TargetLoweringBase {
     return true;
   }
 
+  /// Return whether it is valid to fold a select of two loads into a load
+  /// through a select of their addresses in the given address space.
+  virtual bool shouldFoldSelectOfLoads(unsigned AddressSpace) const {
+    return true;
+  }
+
   /// Does the target have multiple (allocatable) condition registers that
   /// can be used to store the results of comparisons for use by selects
   /// and conditional branches. With multiple condition registers, the code
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index d7db5fb1b9e40..f8c17de1c33b8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -30824,6 +30824,11 @@ bool DAGCombiner::SimplifySelectOps(SDNode *TheSelect, SDValue LHS,
                                       LLD->getBasePtr().getValueType()))
       return false;
 
+    unsigned AddrSpace = LLD->getAddressSpace();
+    assert(AddrSpace == RLD->getAddressSpace());
+    if (!TLI.shouldFoldSelectOfLoads(AddrSpace))
+      return false;
+
     // The loads must not depend on one another.
     if (LLD->isPredecessorOf(RLD) || RLD->isPredecessorOf(LLD))
       return false;
@@ -30899,9 +30904,6 @@ bool DAGCombiner::SimplifySelectOps(SDNode *TheSelect, SDValue LHS,
     // but the new load must be the minimum (most restrictive) alignment of the
     // inputs.
     Align Alignment = std::min(LLD->getAlign(), RLD->getAlign());
-    unsigned AddrSpace = LLD->getAddressSpace();
-    assert(AddrSpace == RLD->getAddressSpace());
-
     MachineMemOperand::Flags MMOFlags = LLD->getMemOperand()->getFlags();
     if (!RLD->isInvariant())
       MMOFlags &= ~MachineMemOperand::MOInvariant;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 8c14ffced3425..18da9a91882d8 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -1055,6 +1055,11 @@ bool WebAssemblyTargetLowering::isVectorLoadExtDesirable(SDValue ExtVal) const {
          (ExtT == MVT::v2i64 && MemT == MVT::v2i32);
 }
 
+bool WebAssemblyTargetLowering::shouldFoldSelectOfLoads(
+    unsigned AddressSpace) const {
+  return !WebAssembly::isWasmVarAddressSpace(AddressSpace);
+}
+
 bool WebAssemblyTargetLowering::isOffsetFoldingLegal(
     const GlobalAddressSDNode *GA) const {
   // Wasm doesn't support function addresses with offsets
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 04e6d6f2d9367..5d0b31693a2a3 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -56,6 +56,7 @@ class WebAssemblyTargetLowering final : public TargetLowering {
   bool isIntDivCheap(EVT VT, AttributeList Attr) const override;
   bool isVectorLoadExtDesirable(SDValue ExtVal) const override;
   bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override;
+  bool shouldFoldSelectOfLoads(unsigned AddressSpace) const override;
   EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context,
                          EVT VT) const override;
   void getTgtMemIntrinsic(SmallVectorImpl<IntrinsicInfo> &Infos,
diff --git a/llvm/test/CodeGen/WebAssembly/global-select-load.ll b/llvm/test/CodeGen/WebAssembly/global-select-load.ll
new file mode 100644
index 0000000000000..d57c7b59cb7c8
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-select-load.ll
@@ -0,0 +1,30 @@
+; RUN: llc < %s -O2 -mtriple=wasm32-unknown-unknown | FileCheck %s
+
+; Do not fold a select of wasm_var loads into a load through a select of their
+; addresses. WebAssembly globals are accessed by name, not computed addresses.
+
+ at g1 = external addrspace(1) global i32
+ at g2 = external addrspace(1) global i32
+
+define i32 @select_global_load(i1 %cond) {
+; CHECK-LABEL: select_global_load:
+; CHECK:       global.get g1
+; CHECK:       global.get g2
+; CHECK:       i32.select
+  %g1_value = load i32, ptr addrspace(1) @g1
+  %g2_value = load i32, ptr addrspace(1) @g2
+  %result = select i1 %cond, i32 %g1_value, i32 %g2_value
+  ret i32 %result
+}
+
+define i32 @selectcc_global_load(i32 %cond) {
+; CHECK-LABEL: selectcc_global_load:
+; CHECK:       global.get g1
+; CHECK:       global.get g2
+; CHECK:       i32.select
+  %nonzero = icmp ne i32 %cond, 0
+  %g1_value = load i32, ptr addrspace(1) @g1
+  %g2_value = load i32, ptr addrspace(1) @g2
+  %result = select i1 %nonzero, i32 %g1_value, i32 %g2_value
+  ret i32 %result
+}



More information about the llvm-commits mailing list