[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