Skip to content

[WebAssembly] Fix crash when storing externref to globals#178474

Closed
ParkHanbum wants to merge 1 commit intollvm:mainfrom
ParkHanbum:i141011
Closed

[WebAssembly] Fix crash when storing externref to globals#178474
ParkHanbum wants to merge 1 commit intollvm:mainfrom
ParkHanbum:i141011

Conversation

@ParkHanbum
Copy link
Copy Markdown
Contributor

Storing externref values to global variables previously caused a selection error because the backend attempted a linear memory store.

This patch implements custom lowering for ISD::STORE to convert stores of externref values into WebAssemblyISD::GLOBAL_SET, ensuring they are correctly updated in the Wasm global space.

Fixes: #141011

Storing `externref` values to global variables previously caused
a selection error because the backend attempted a linear memory store.

This patch implements custom lowering for `ISD::STORE` to convert
stores of `externref` values into `WebAssemblyISD::GLOBAL_SET`,
ensuring they are correctly updated in the Wasm global space.

Fixes: llvm#141011
@llvmbot
Copy link
Copy Markdown
Member

llvmbot commented Jan 28, 2026

@llvm/pr-subscribers-backend-webassembly

Author: hanbeom (ParkHanbum)

Changes

Storing externref values to global variables previously caused a selection error because the backend attempted a linear memory store.

This patch implements custom lowering for ISD::STORE to convert stores of externref values into WebAssemblyISD::GLOBAL_SET, ensuring they are correctly updated in the Wasm global space.

Fixes: #141011


Full diff: https://github.com/llvm/llvm-project/pull/178474.diff

3 Files Affected:

  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp (+17)
  • (modified) llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td (+5)
  • (modified) llvm/test/CodeGen/WebAssembly/global-set.ll (+13)
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 5abf0e8f59d2a..69357c908397b 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -1824,6 +1824,23 @@ SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
         "Encountered an unlowerable store to the wasm_var address space",
         false);
 
+  if (Value.getValueType() == MVT::externref) {
+    SDValue Ptr =
+        Base.getOpcode() != WebAssemblyISD::Wrapper ? Base : Base.getOperand(0);
+    auto *GNode = dyn_cast<GlobalAddressSDNode>(Ptr);
+    if (!GNode)
+      report_fatal_error("Cannot store externref to non-global address");
+
+    const GlobalValue *GV = GNode->getGlobal();
+    SDValue TargetGlobal =
+        DAG.getTargetGlobalAddress(GV, DL, Ptr.getValueType());
+    SDValue WrappedPtr =
+        DAG.getNode(WebAssemblyISD::Wrapper, DL, MVT::i32, TargetGlobal);
+
+    return DAG.getNode(WebAssemblyISD::GLOBAL_SET, DL, MVT::Other,
+                       SN->getChain(), Value, WrappedPtr);
+  }
+
   return Op;
 }
 
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 76e94c2ec3d61..839f5e11fe6d2 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -458,6 +458,11 @@ def : Pat<(i32 (WebAssemblyWrapperREL texternalsym:$addr)),
 def : Pat<(i64 (WebAssemblyWrapperREL texternalsym:$addr)),
           (CONST_I64 texternalsym:$addr)>, Requires<[IsPIC, HasAddr64]>;
 
+// Manually define the pattern because 'externref' is not included in the
+// standard RegTypes iteration of the generic loop above.
+def : Pat<(WebAssemblyglobal_set externref:$src, (WebAssemblyWrapper tglobaladdr:$addr)),
+          (GLOBAL_SET_EXTERNREF tglobaladdr:$addr, externref:$src)>,
+          Requires<[HasReferenceTypes]>;
 //===----------------------------------------------------------------------===//
 // Additional sets of instructions.
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/CodeGen/WebAssembly/global-set.ll b/llvm/test/CodeGen/WebAssembly/global-set.ll
index 4553957ae7588..6c37ba8e7ac1c 100644
--- a/llvm/test/CodeGen/WebAssembly/global-set.ll
+++ b/llvm/test/CodeGen/WebAssembly/global-set.ll
@@ -4,6 +4,19 @@
 @i64_global = local_unnamed_addr addrspace(1) global i64 undef
 @f32_global = local_unnamed_addr addrspace(1) global float undef
 @f64_global = local_unnamed_addr addrspace(1) global double undef
+@a = global ptr addrspace(10) null
+
+declare ptr addrspace(10) @function_that_gives_me_a_js_object()
+define void @object_store_to_global_ptr() {
+; CHECK-LABEL: object_store_to_global_ptr:
+; CHECK:         .functype object_store_to_global_ptr () -> ()
+; CHECK-NEXT:    call function_that_gives_me_a_js_object
+; CHECK-NEXT:    global.set a
+; CHECK-NEXT:    end_function
+  %object = call ptr addrspace(10) @function_that_gives_me_a_js_object()
+  store ptr addrspace(10) %object, ptr @a
+  ret void
+}
 
 define void @set_i32_global(i32 %v) {
 ; CHECK-LABEL: set_i32_global:

@ParkHanbum ParkHanbum requested review from dschuff, lukel97 and valadaptive and removed request for lukel97 January 28, 2026 18:11
@QuantumSegfault
Copy link
Copy Markdown
Contributor

QuantumSegfault commented Jan 29, 2026

None of these changes are necessary.

The problem is that in the test, the global @a is declared a global ptr addrspace(10), which is indeed a LLVM global with type externref. However the global exists in the default address space (0), which is WASM linear memory (which is invalid; you can't really store an externref into linear memory). You are trying to create a WASM global with externref as the type, which means the LLVM global must be in addrspace(1).

The test should be this instead:

@a = addrspace(1) global ptr addrspace(10) null

declare ptr addrspace(10) @function_that_gives_me_a_js_object()
define void @object_store_to_global_ptr() {
; CHECK-LABEL: object_store_to_global_ptr:
; CHECK:         .functype object_store_to_global_ptr () -> ()
; CHECK-NEXT:    call function_that_gives_me_a_js_object
; CHECK-NEXT:    global.set a
; CHECK-NEXT:    end_function
  %object = call ptr addrspace(10) @function_that_gives_me_a_js_object()
  store ptr addrspace(10) %object, ptr addrspace(1) @a
  ret void
}

Address space 1 is how LLVM represents WASM globals (and tables).

@ParkHanbum ParkHanbum closed this Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WebAssembly][Clang] _externref_t usage in global scope emits fatal error in backend

3 participants