https://github.com/MaxDesiatov updated 
https://github.com/llvm/llvm-project/pull/212007

>From 2b56c5300174cd084b4dca696899bd1dea04b9cd Mon Sep 17 00:00:00 2001
From: Max Desiatov <[email protected]>
Date: Sat, 25 Jul 2026 10:50:33 +0100
Subject: [PATCH 1/2] [WebAssembly] Emit constant initializers for addrspace(1)
 globals

A Wasm global in `addrspace(1)` lost its initializer, because the object writer 
hardcoded 0 into the Global section's init expression. This carries the 
constant's raw bits on `MCSymbolWasm` so the writer emits the real 
i32/i64/f32/f64 value. Added `llvm/test/CodeGen/WebAssembly/global-init.ll`, 
`clang/test/CodeGen/WebAssembly/wasm-globals.c`, and 
`lld/test/wasm/global-init.ll`.

# Conflicts:
#       llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
---
 clang/test/CodeGen/WebAssembly/wasm-globals.c | 43 ++++++++++++++++++
 lld/test/wasm/global-init.ll                  | 26 +++++++++++
 llvm/include/llvm/MC/MCSymbolWasm.h           |  8 ++++
 llvm/lib/MC/WasmObjectWriter.cpp              | 14 ++++--
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     | 32 +++++++++++++-
 llvm/test/CodeGen/WebAssembly/global-init.ll  | 44 +++++++++++++++++++
 6 files changed, 161 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CodeGen/WebAssembly/wasm-globals.c
 create mode 100644 lld/test/wasm/global-init.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/global-init.ll

diff --git a/clang/test/CodeGen/WebAssembly/wasm-globals.c 
b/clang/test/CodeGen/WebAssembly/wasm-globals.c
new file mode 100644
index 0000000000000..31378f0c862c2
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/wasm-globals.c
@@ -0,0 +1,43 @@
+// REQUIRES: webassembly-registered-target
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=IR
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -S -o - %s | FileCheck %s --check-prefix=ASM
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -emit-obj -o %t.o %s
+// RUN: obj2yaml %t.o | FileCheck %s --check-prefix=OBJ
+
+// IR: @mut = {{.*}}addrspace(1) global i32 33554435
+int mut __attribute__((address_space(1))) = 0x02000003;
+
+// Without volatile the load of a constant global folds to i32.const, hiding
+// the global.get.
+// IR: @immut = {{.*}}addrspace(1) constant i32 42
+const volatile int immut __attribute__((address_space(1))) = 42;
+
+// ASM-LABEL: read_mut:
+// ASM: global.get mut{{$}}
+int read_mut(void) { return mut; }
+
+// ASM-LABEL: write_mut:
+// ASM: global.set mut{{$}}
+void write_mut(int x) { mut = x; }
+
+// ASM-LABEL: read_immut:
+// ASM: global.get immut{{$}}
+int read_immut(void) { return immut; }
+
+// ASM: .globaltype mut, i32{{$}}
+// ASM: .globaltype immut, i32, immutable{{$}}
+
+// OBJ:      - Type:            GLOBAL
+// OBJ-NEXT:   Globals:
+// OBJ-NEXT:     - Index:           0
+// OBJ-NEXT:       Type:            I32
+// OBJ-NEXT:       Mutable:         true
+// OBJ-NEXT:       InitExpr:
+// OBJ-NEXT:         Opcode:          I32_CONST
+// OBJ-NEXT:         Value:           33554435
+// OBJ-NEXT:     - Index:           1
+// OBJ-NEXT:       Type:            I32
+// OBJ-NEXT:       Mutable:         false
+// OBJ-NEXT:       InitExpr:
+// OBJ-NEXT:         Opcode:          I32_CONST
+// OBJ-NEXT:         Value:           42
diff --git a/lld/test/wasm/global-init.ll b/lld/test/wasm/global-init.ll
new file mode 100644
index 0000000000000..b6f1be2682dce
--- /dev/null
+++ b/lld/test/wasm/global-init.ll
@@ -0,0 +1,26 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown %s -o %t.o
+; RUN: wasm-ld --no-entry --export=use --no-gc-sections %t.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; The constant initializer of an address-space-1 Wasm global must survive
+; linking, and the global's name must appear in the "name" custom section.
+
+@gv = hidden addrspace(1) global i32 33554435
+define i32 @use() {
+  %v = load i32, ptr addrspace(1) @gv
+  ret i32 %v
+}
+
+; CHECK:      - Type:            GLOBAL
+; CHECK:          - Index:           1
+; CHECK-NEXT:       Type:            I32
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          I32_CONST
+; CHECK-NEXT:         Value:           33554435
+
+; CHECK:      - Type:            CUSTOM
+; CHECK:        Name:            name
+; CHECK:        GlobalNames:
+; CHECK:          - Index:           1
+; CHECK:            Name:            gv
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h 
b/llvm/include/llvm/MC/MCSymbolWasm.h
index 5c9f14e5e6d64..e18dbc29ac786 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -28,6 +28,7 @@ class MCSymbolWasm : public MCSymbol {
   wasm::WasmSignature *Signature = nullptr;
   std::optional<wasm::WasmGlobalType> GlobalType;
   std::optional<wasm::WasmTableType> TableType;
+  std::optional<uint64_t> GlobalInitValue;
 
   /// An expression describing how to calculate the size of a symbol. If a
   /// symbol has no size this field will be NULL.
@@ -138,6 +139,13 @@ class MCSymbolWasm : public MCSymbol {
   }
   void setGlobalType(wasm::WasmGlobalType GT) { GlobalType = GT; }
 
+  bool hasGlobalInitValue() const { return GlobalInitValue.has_value(); }
+  uint64_t getGlobalInitValue() const {
+    assert(GlobalInitValue);
+    return *GlobalInitValue;
+  }
+  void setGlobalInitValue(uint64_t V) { GlobalInitValue = V; }
+
   bool hasTableType() const { return TableType.has_value(); }
   const wasm::WasmTableType &getTableType() const {
     assert(hasTableType());
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index 43f71cc43bf8f..f8b1dd174e8c7 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -919,16 +919,16 @@ void 
WasmObjectWriter::writeGlobalSection(ArrayRef<wasm::WasmGlobal> Globals) {
       W->OS << char(Global.InitExpr.Inst.Opcode);
       switch (Global.Type.Type) {
       case wasm::WASM_TYPE_I32:
-        encodeSLEB128(0, W->OS);
+        encodeSLEB128(Global.InitExpr.Inst.Value.Int32, W->OS);
         break;
       case wasm::WASM_TYPE_I64:
-        encodeSLEB128(0, W->OS);
+        encodeSLEB128(Global.InitExpr.Inst.Value.Int64, W->OS);
         break;
       case wasm::WASM_TYPE_F32:
-        writeI32(0);
+        writeI32(Global.InitExpr.Inst.Value.Float32);
         break;
       case wasm::WASM_TYPE_F64:
-        writeI64(0);
+        writeI64(Global.InitExpr.Inst.Value.Float64);
         break;
       case wasm::WASM_TYPE_EXTERNREF:
         writeValueType(wasm::ValType::EXTERNREF);
@@ -1666,18 +1666,24 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler 
&Asm,
           Global.Type = WS.getGlobalType();
           Global.Index = NumGlobalImports + Globals.size();
           Global.InitExpr.Extended = false;
+          uint64_t Bits = WS.hasGlobalInitValue() ? WS.getGlobalInitValue() : 
0;
           switch (Global.Type.Type) {
           case wasm::WASM_TYPE_I32:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
+            Global.InitExpr.Inst.Value.Int32 =
+                static_cast<int32_t>(static_cast<uint32_t>(Bits));
             break;
           case wasm::WASM_TYPE_I64:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_I64_CONST;
+            Global.InitExpr.Inst.Value.Int64 = static_cast<int64_t>(Bits);
             break;
           case wasm::WASM_TYPE_F32:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_F32_CONST;
+            Global.InitExpr.Inst.Value.Float32 = static_cast<uint32_t>(Bits);
             break;
           case wasm::WASM_TYPE_F64:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_F64_CONST;
+            Global.InitExpr.Inst.Value.Float64 = Bits;
             break;
           case wasm::WASM_TYPE_EXTERNREF:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_REF_NULL;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 84bab9487f0c8..26e224a597540 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -41,6 +41,7 @@
 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
 #include "llvm/CodeGen/MachinePassManager.h"
 #include "llvm/IR/Analysis.h"
+#include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/GlobalVariable.h"
@@ -189,6 +190,26 @@ MCSymbolWasm 
*WebAssemblyAsmPrinter::getMCSymbolForFunction(
   return WasmSym;
 }
 
+static std::optional<uint64_t> getWasmGlobalInitBits(const Constant *C) {
+  // Sub-i32 ints and half/bfloat legalize to a single i32/f32 MVT and so pass
+  // wasmSymbolSetType(), but their bits are not an i32/f32 init value; gate on
+  // the exact IR type so they fall back to the default 0 rather than emit a
+  // garbage init expr.
+  if (const auto *CI = dyn_cast<ConstantInt>(C)) {
+    unsigned BW = CI->getType()->getIntegerBitWidth();
+    if (BW == 32 || BW == 64)
+      return CI->getValue().getZExtValue();
+    return std::nullopt;
+  }
+  if (const auto *CF = dyn_cast<ConstantFP>(C)) {
+    Type *T = CF->getType();
+    if (T->isFloatTy() || T->isDoubleTy())
+      return CF->getValueAPF().bitcastToAPInt().getZExtValue();
+    return std::nullopt;
+  }
+  return std::nullopt;
+}
+
 void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
   if (GV->hasAttribute("wasm-import-module") ||
       GV->hasAttribute("wasm-import-name")) {
@@ -236,6 +257,15 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const 
GlobalVariable *GV) {
   }
 
   emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration());
+
+  // WasmObjectWriter reads these init bits back off the symbol to emit the 
init
+  // expression instead of the type's default 0.
+  if (GV->hasInitializer() && Sym->isGlobal()) {
+    if (std::optional<uint64_t> Bits =
+            getWasmGlobalInitBits(GV->getInitializer()))
+      Sym->setGlobalInitValue(*Bits);
+  }
+
   emitSymbolType(Sym);
   if (GV->isDeclaration()) {
     if (GV->hasAttribute("wasm-import-module")) {
@@ -261,8 +291,6 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const 
GlobalVariable *GV) {
       Sym->setExportName(OutContext.allocateString(ExportName));
       getTargetStreamer()->emitExportName(Sym, ExportName);
     }
-    // TODO: Actually emit the initializer value.  Otherwise the global has the
-    // default value for its type (0, ref.null, etc).
     OutStreamer->addBlankLine();
   }
 }
diff --git a/llvm/test/CodeGen/WebAssembly/global-init.ll 
b/llvm/test/CodeGen/WebAssembly/global-init.ll
new file mode 100644
index 0000000000000..72e5c180c649c
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-init.ll
@@ -0,0 +1,44 @@
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -filetype=obj | obj2yaml | 
FileCheck %s
+
+; No function is defined here, so the AsmPrinter's Subtarget is null: emitting
+; these address-space-1 globals must not crash, and each initializer must reach
+; the Global section instead of the type's default 0.
+
+@i32v = addrspace(1) global i32 33554435
+@i64v = addrspace(1) global i64 8589934595
+@f32v = addrspace(1) global float 2.0
+@f64v = addrspace(1) global double 2.0
+@constv = addrspace(1) constant i32 42
+
+; CHECK:      - Type:            GLOBAL
+; CHECK-NEXT:   Globals:
+; CHECK-NEXT:     - Index:           0
+; CHECK-NEXT:       Type:            I32
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          I32_CONST
+; CHECK-NEXT:         Value:           33554435
+; CHECK-NEXT:     - Index:           1
+; CHECK-NEXT:       Type:            I64
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          I64_CONST
+; CHECK-NEXT:         Value:           8589934595
+; CHECK-NEXT:     - Index:           2
+; CHECK-NEXT:       Type:            F32
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          F32_CONST
+; CHECK-NEXT:         Value:           1073741824
+; CHECK-NEXT:     - Index:           3
+; CHECK-NEXT:       Type:            F64
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          F64_CONST
+; CHECK-NEXT:         Value:           4611686018427387904
+; CHECK-NEXT:     - Index:           4
+; CHECK-NEXT:       Type:            I32
+; CHECK-NEXT:       Mutable:         false
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          I32_CONST
+; CHECK-NEXT:         Value:           42

>From c931291759acfe6bd264b69c151ab6aa21be14fc Mon Sep 17 00:00:00 2001
From: Max Desiatov <[email protected]>
Date: Sun, 11 Oct 2026 22:19:42 +0100
Subject: [PATCH 2/2] [WebAssembly] Add a .globalinit directive and diagnose
 unsupported global initializers

Store the value as a `wasm::WasmInitExpr` on `MCSymbolWasm`, which the asm 
streamer prints and the asm parser reads as `.globalinit SYM, <type>.const 
VALUE`. An unsupported initializer now triggers an error.
---
 clang/test/CodeGen/WebAssembly/wasm-globals.c |  19 +-
 lld/test/wasm/global-init.ll                  |   7 +-
 llvm/include/llvm/MC/MCSymbolWasm.h           |  14 +-
 llvm/lib/MC/WasmObjectWriter.cpp              |  63 ++--
 .../AsmParser/WebAssemblyAsmParser.cpp        | 170 ++++++++++-
 .../MCTargetDesc/WebAssemblyInstPrinter.cpp   |  30 +-
 .../WebAssemblyMCTypeUtilities.cpp            |  53 ++++
 .../MCTargetDesc/WebAssemblyMCTypeUtilities.h |  16 +
 .../WebAssemblyTargetStreamer.cpp             |  31 ++
 .../MCTargetDesc/WebAssemblyTargetStreamer.h  |   5 +
 .../WebAssembly/WebAssemblyAsmPrinter.cpp     |  70 +++--
 .../CodeGen/WebAssembly/global-addrspace.ll   |   5 +
 .../WebAssembly/global-init-invalid.ll        |  35 +++
 .../WebAssembly/global-init-reftype.ll        |  22 ++
 llvm/test/CodeGen/WebAssembly/global-init.ll  | 163 ++++++++---
 .../WebAssembly/global-unsupported-type.ll    |  12 +
 llvm/test/MC/WebAssembly/globalinit-invalid.s |  77 +++++
 .../MC/WebAssembly/globalinit-not-global.s    |  32 ++
 .../MC/WebAssembly/globalinit-undefined.s     |  22 ++
 llvm/test/MC/WebAssembly/globalinit.s         | 274 ++++++++++++++++++
 .../MC/WebAssembly/globaltype-unsupported.s   |   8 +
 21 files changed, 989 insertions(+), 139 deletions(-)
 create mode 100644 llvm/test/CodeGen/WebAssembly/global-init-invalid.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/global-init-reftype.ll
 create mode 100644 llvm/test/CodeGen/WebAssembly/global-unsupported-type.ll
 create mode 100644 llvm/test/MC/WebAssembly/globalinit-invalid.s
 create mode 100644 llvm/test/MC/WebAssembly/globalinit-not-global.s
 create mode 100644 llvm/test/MC/WebAssembly/globalinit-undefined.s
 create mode 100644 llvm/test/MC/WebAssembly/globalinit.s
 create mode 100644 llvm/test/MC/WebAssembly/globaltype-unsupported.s

diff --git a/clang/test/CodeGen/WebAssembly/wasm-globals.c 
b/clang/test/CodeGen/WebAssembly/wasm-globals.c
index 31378f0c862c2..24437005523d8 100644
--- a/clang/test/CodeGen/WebAssembly/wasm-globals.c
+++ b/clang/test/CodeGen/WebAssembly/wasm-globals.c
@@ -1,8 +1,6 @@
 // REQUIRES: webassembly-registered-target
 // RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=IR
 // RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -S -o - %s | FileCheck %s --check-prefix=ASM
-// RUN: %clang_cc1 -triple wasm32-unknown-unknown -mrelocation-model static 
-O2 -emit-obj -o %t.o %s
-// RUN: obj2yaml %t.o | FileCheck %s --check-prefix=OBJ
 
 // IR: @mut = {{.*}}addrspace(1) global i32 33554435
 int mut __attribute__((address_space(1))) = 0x02000003;
@@ -25,19 +23,6 @@ void write_mut(int x) { mut = x; }
 int read_immut(void) { return immut; }
 
 // ASM: .globaltype mut, i32{{$}}
+// ASM-NEXT: .globalinit mut, i32.const 33554435{{$}}
 // ASM: .globaltype immut, i32, immutable{{$}}
-
-// OBJ:      - Type:            GLOBAL
-// OBJ-NEXT:   Globals:
-// OBJ-NEXT:     - Index:           0
-// OBJ-NEXT:       Type:            I32
-// OBJ-NEXT:       Mutable:         true
-// OBJ-NEXT:       InitExpr:
-// OBJ-NEXT:         Opcode:          I32_CONST
-// OBJ-NEXT:         Value:           33554435
-// OBJ-NEXT:     - Index:           1
-// OBJ-NEXT:       Type:            I32
-// OBJ-NEXT:       Mutable:         false
-// OBJ-NEXT:       InitExpr:
-// OBJ-NEXT:         Opcode:          I32_CONST
-// OBJ-NEXT:         Value:           42
+// ASM-NEXT: .globalinit immut, i32.const 42{{$}}
diff --git a/lld/test/wasm/global-init.ll b/lld/test/wasm/global-init.ll
index b6f1be2682dce..9c3582b0da310 100644
--- a/lld/test/wasm/global-init.ll
+++ b/lld/test/wasm/global-init.ll
@@ -1,11 +1,14 @@
-; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown %s -o %t.o
+; RUN: llc -filetype=obj %s -o %t.o
 ; RUN: wasm-ld --no-entry --export=use --no-gc-sections %t.o -o %t.wasm
 ; RUN: obj2yaml %t.wasm | FileCheck %s
 
 ; The constant initializer of an address-space-1 Wasm global must survive
 ; linking, and the global's name must appear in the "name" custom section.
 
+target triple = "wasm32-unknown-unknown"
+
 @gv = hidden addrspace(1) global i32 33554435
+
 define i32 @use() {
   %v = load i32, ptr addrspace(1) @gv
   ret i32 %v
@@ -23,4 +26,4 @@ define i32 @use() {
 ; CHECK:        Name:            name
 ; CHECK:        GlobalNames:
 ; CHECK:          - Index:           1
-; CHECK:            Name:            gv
+; CHECK-NEXT:       Name:            gv
diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h 
b/llvm/include/llvm/MC/MCSymbolWasm.h
index e18dbc29ac786..25f1eec733b68 100644
--- a/llvm/include/llvm/MC/MCSymbolWasm.h
+++ b/llvm/include/llvm/MC/MCSymbolWasm.h
@@ -28,7 +28,8 @@ class MCSymbolWasm : public MCSymbol {
   wasm::WasmSignature *Signature = nullptr;
   std::optional<wasm::WasmGlobalType> GlobalType;
   std::optional<wasm::WasmTableType> TableType;
-  std::optional<uint64_t> GlobalInitValue;
+  /// A global's initial value, or its type's default when unset.
+  std::optional<wasm::WasmInitExpr> GlobalInitExpr;
 
   /// An expression describing how to calculate the size of a symbol. If a
   /// symbol has no size this field will be NULL.
@@ -133,18 +134,19 @@ class MCSymbolWasm : public MCSymbol {
   const wasm::WasmSignature *getSignature() const { return Signature; }
   void setSignature(wasm::WasmSignature *Sig) { Signature = Sig; }
 
+  bool hasGlobalType() const { return GlobalType.has_value(); }
   const wasm::WasmGlobalType &getGlobalType() const {
     assert(GlobalType);
     return *GlobalType;
   }
   void setGlobalType(wasm::WasmGlobalType GT) { GlobalType = GT; }
 
-  bool hasGlobalInitValue() const { return GlobalInitValue.has_value(); }
-  uint64_t getGlobalInitValue() const {
-    assert(GlobalInitValue);
-    return *GlobalInitValue;
+  bool hasGlobalInitExpr() const { return GlobalInitExpr.has_value(); }
+  const wasm::WasmInitExpr &getGlobalInitExpr() const {
+    assert(GlobalInitExpr);
+    return *GlobalInitExpr;
   }
-  void setGlobalInitValue(uint64_t V) { GlobalInitValue = V; }
+  void setGlobalInitExpr(wasm::WasmInitExpr IE) { GlobalInitExpr = IE; }
 
   bool hasTableType() const { return TableType.has_value(); }
   const wasm::WasmTableType &getTableType() const {
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index f8b1dd174e8c7..e900ca78af009 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -73,12 +73,6 @@ struct WasmFunction {
   MCSection *Section;
 };
 
-// A wasm global to be written into the global section.
-struct WasmGlobal {
-  wasm::WasmGlobalType Type;
-  uint64_t InitialValue;
-};
-
 // Information about a single item which is part of a COMDAT.  For each data
 // segment or function which is in the COMDAT, there is a corresponding
 // WasmComdatEntry.
@@ -322,6 +316,8 @@ class WasmObjectWriter : public MCObjectWriter {
 
   void writeValueType(wasm::ValType Ty) { W->OS << static_cast<char>(Ty); }
 
+  void reportUnsupportedGlobal(const MCSymbolWasm &WS, uint8_t Type);
+
   void writeTypeSection(ArrayRef<wasm::WasmSignature> Signatures);
   void writeImportSection(ArrayRef<wasm::WasmImport> Imports, uint64_t 
DataSize,
                           uint32_t NumElements);
@@ -902,6 +898,16 @@ void WasmObjectWriter::writeTagSection(ArrayRef<uint32_t> 
TagTypes) {
   endSection(Section);
 }
 
+void WasmObjectWriter::reportUnsupportedGlobal(const MCSymbolWasm &WS,
+                                               uint8_t Type) {
+  // llvm/lib/MC cannot call the WebAssembly target's typeToString.
+  getContext().reportError(SMLoc(),
+                           "WebAssembly global '" + WS.getName() +
+                               "' has a type that cannot be initialized "
+                               "(type 0x" +
+                               Twine::utohexstr(Type) + ")");
+}
+
 void WasmObjectWriter::writeGlobalSection(ArrayRef<wasm::WasmGlobal> Globals) {
   if (Globals.empty())
     return;
@@ -917,24 +923,24 @@ void 
WasmObjectWriter::writeGlobalSection(ArrayRef<wasm::WasmGlobal> Globals) {
       llvm_unreachable("extected init expressions not supported");
     } else {
       W->OS << char(Global.InitExpr.Inst.Opcode);
-      switch (Global.Type.Type) {
-      case wasm::WASM_TYPE_I32:
+      switch (Global.InitExpr.Inst.Opcode) {
+      case wasm::WASM_OPCODE_I32_CONST:
         encodeSLEB128(Global.InitExpr.Inst.Value.Int32, W->OS);
         break;
-      case wasm::WASM_TYPE_I64:
+      case wasm::WASM_OPCODE_I64_CONST:
         encodeSLEB128(Global.InitExpr.Inst.Value.Int64, W->OS);
         break;
-      case wasm::WASM_TYPE_F32:
+      case wasm::WASM_OPCODE_F32_CONST:
         writeI32(Global.InitExpr.Inst.Value.Float32);
         break;
-      case wasm::WASM_TYPE_F64:
+      case wasm::WASM_OPCODE_F64_CONST:
         writeI64(Global.InitExpr.Inst.Value.Float64);
         break;
-      case wasm::WASM_TYPE_EXTERNREF:
-        writeValueType(wasm::ValType::EXTERNREF);
+      case wasm::WASM_OPCODE_REF_NULL:
+        writeValueType(static_cast<wasm::ValType>(Global.Type.Type));
         break;
       default:
-        llvm_unreachable("unexpected type");
+        llvm_unreachable("unexpected init expression opcode");
       }
     }
     W->OS << char(wasm::WASM_OPCODE_END);
@@ -1386,6 +1392,11 @@ void WasmObjectWriter::prepareImports(
       } else if (WS.isGlobal()) {
         if (WS.isWeak())
           report_fatal_error("undefined global symbol cannot be weak");
+        if (WS.hasGlobalInitExpr())
+          getContext().reportError(SMLoc(), "WebAssembly global '" +
+                                                WS.getName() +
+                                                "' has an initial value but "
+                                                "is not defined");
 
         wasm::WasmImport Import;
         Import.Field = WS.getImportName();
@@ -1666,30 +1677,40 @@ uint64_t WasmObjectWriter::writeOneObject(MCAssembler 
&Asm,
           Global.Type = WS.getGlobalType();
           Global.Index = NumGlobalImports + Globals.size();
           Global.InitExpr.Extended = false;
-          uint64_t Bits = WS.hasGlobalInitValue() ? WS.getGlobalInitValue() : 
0;
           switch (Global.Type.Type) {
           case wasm::WASM_TYPE_I32:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
-            Global.InitExpr.Inst.Value.Int32 =
-                static_cast<int32_t>(static_cast<uint32_t>(Bits));
+            Global.InitExpr.Inst.Value.Int32 = 0;
             break;
           case wasm::WASM_TYPE_I64:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_I64_CONST;
-            Global.InitExpr.Inst.Value.Int64 = static_cast<int64_t>(Bits);
+            Global.InitExpr.Inst.Value.Int64 = 0;
             break;
           case wasm::WASM_TYPE_F32:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_F32_CONST;
-            Global.InitExpr.Inst.Value.Float32 = static_cast<uint32_t>(Bits);
+            Global.InitExpr.Inst.Value.Float32 = 0;
             break;
           case wasm::WASM_TYPE_F64:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_F64_CONST;
-            Global.InitExpr.Inst.Value.Float64 = Bits;
+            Global.InitExpr.Inst.Value.Float64 = 0;
             break;
           case wasm::WASM_TYPE_EXTERNREF:
             Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_REF_NULL;
             break;
           default:
-            llvm_unreachable("unexpected type");
+            // Keep the entry: relocations and the symbol table look up this
+            // global's index in WasmIndices. The error discards the object.
+            reportUnsupportedGlobal(WS, Global.Type.Type);
+            Global.Type.Type = uint8_t(wasm::ValType::I32);
+            Global.InitExpr.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
+            Global.InitExpr.Inst.Value.Int32 = 0;
+            break;
+          }
+          if (WS.hasGlobalInitExpr()) {
+            assert(WS.getGlobalInitExpr().Inst.Opcode ==
+                       Global.InitExpr.Inst.Opcode &&
+                   "global init expression does not match the global's type");
+            Global.InitExpr = WS.getGlobalInitExpr();
           }
           assert(!WasmIndices.contains(&WS));
           WasmIndices[&WS] = Global.Index;
diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp 
b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 475c73c6f81b2..ff4cf4c4169d1 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -19,6 +19,8 @@
 #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"
 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
 #include "TargetInfo/WebAssemblyTargetInfo.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/MapVector.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCInst.h"
@@ -237,6 +239,26 @@ static wasm::WasmLimits defaultLimits() {
   return {wasm::WASM_LIMITS_FLAG_NONE, 0, 0, 0};
 }
 
+// Compare only the union member the opcode selects: bytes past it are
+// uninitialized.
+static bool sameInitExpr(const wasm::WasmInitExprMVP &A,
+                         const wasm::WasmInitExprMVP &B) {
+  if (A.Opcode != B.Opcode)
+    return false;
+  switch (A.Opcode) {
+  case wasm::WASM_OPCODE_I32_CONST:
+    return A.Value.Int32 == B.Value.Int32;
+  case wasm::WASM_OPCODE_I64_CONST:
+    return A.Value.Int64 == B.Value.Int64;
+  case wasm::WASM_OPCODE_F32_CONST:
+    return A.Value.Float32 == B.Value.Float32;
+  case wasm::WASM_OPCODE_F64_CONST:
+    return A.Value.Float64 == B.Value.Float64;
+  default:
+    llvm_unreachable("only number types carry an init expression");
+  }
+}
+
 static MCSymbolWasm *getOrCreateFunctionTableSymbol(MCContext &Ctx,
                                                     const StringRef &Name,
                                                     bool Is64) {
@@ -292,6 +314,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser 
{
 
   MCSymbolWasm *DefaultFunctionTable = nullptr;
   MCSymbol *LastFunctionLabel = nullptr;
+  MapVector<MCSymbolWasm *, SMLoc> GlobalInitLocs;
 
   bool Is64;
 
@@ -470,25 +493,95 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
     Parser.Lex();
   }
 
-  bool parseSingleFloat(bool IsNegative, OperandVector &Operands) {
+  // Parses a global's initial value, spelled like the matching const
+  // instruction's immediate: `i32.const 42`, `f32.const 0x1p1`.
+  bool parseGlobalInit(wasm::ValType Type,
+                       std::optional<wasm::WasmInitExpr> &Result) {
+    auto MnemonicTok = Lexer.getTok();
+    auto Mnemonic = expectIdent();
+    if (Mnemonic.empty())
+      return true;
+    StringRef TypeName = Mnemonic;
+    std::optional<wasm::ValType> InitType;
+    if (TypeName.consume_back(".const"))
+      InitType = WebAssembly::parseType(TypeName);
+    if (!InitType)
+      return error("Expected a const instruction in .globalinit: ",
+                   MnemonicTok);
+    if (*InitType != Type)
+      return error("Initial value does not match the global's type: ",
+                   MnemonicTok);
+    if (!WebAssembly::isNumType(Type))
+      return error("Initial value is not supported for this global type: ",
+                   MnemonicTok);
+
+    SMLoc SignLoc = Lexer.getTok().getLoc();
+    bool Negative = isNext(AsmToken::Minus);
+    auto ValueTok = Lexer.getTok();
+    uint64_t Bits;
+    if (Type == wasm::ValType::I32 || Type == wasm::ValType::I64) {
+      if (ValueTok.isNot(AsmToken::Integer) && 
ValueTok.isNot(AsmToken::BigNum))
+        return error("Expected integer constant, instead got: ", ValueTok);
+      // Accept either the signed or the unsigned spelling of a value of this
+      // width, the way the fixed-width integer directives do.
+      APInt Val = ValueTok.getAPIntVal();
+      Val = Val.zext(Val.getBitWidth() + 1);
+      if (Negative)
+        Val.negate();
+      unsigned Width = Type == wasm::ValType::I32 ? 32 : 64;
+      if (!Val.isIntN(Width) && !Val.isSignedIntN(Width))
+        return error("Integer constant does not fit in the global's type: " +
+                         Twine(Negative ? "-" : "") + ValueTok.getString(),
+                     Negative ? SignLoc : ValueTok.getLoc());
+      Parser.Lex();
+      Bits = Val.trunc(64).getZExtValue();
+    } else {
+      double Val;
+      if (ValueTok.is(AsmToken::Real)) {
+        if (parseSingleFloat(Negative, Val))
+          return true;
+      } else if (parseSpecialFloatMaybe(Negative, Val)) {
+        return error("Expected floating-point constant, instead got: ",
+                     ValueTok);
+      }
+      // A host float conversion need not keep a NaN's sign.
+      APFloat Flt(Val);
+      if (Type == wasm::ValType::F32) {
+        bool LosesInfo;
+        Flt.convert(APFloat::IEEEsingle(), APFloat::rmNearestTiesToEven,
+                    &LosesInfo);
+      }
+      Bits = Flt.bitcastToAPInt().getZExtValue();
+    }
+    Result = WebAssembly::makeGlobalInitExpr(Type, Bits);
+    assert(Result && "a parsed const type must have an init expression");
+    return false;
+  }
+
+  bool parseSingleFloat(bool IsNegative, double &Val) {
     auto &Flt = Lexer.getTok();
-    double Val;
     if (Flt.getString().getAsDouble(Val, false))
       return error("Cannot parse real: ", Flt);
     if (IsNegative)
       Val = -Val;
-    Operands.push_back(std::make_unique<WebAssemblyOperand>(
-        Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{Val}));
     Parser.Lex();
     return false;
   }
 
-  bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {
+  bool parseSingleFloat(bool IsNegative, OperandVector &Operands) {
+    SMLoc Start = Lexer.getTok().getLoc(), End = Lexer.getTok().getEndLoc();
+    double Val;
+    if (parseSingleFloat(IsNegative, Val))
+      return true;
+    Operands.push_back(std::make_unique<WebAssemblyOperand>(
+        Start, End, WebAssemblyOperand::FltOp{Val}));
+    return false;
+  }
+
+  bool parseSpecialFloatMaybe(bool IsNegative, double &Val) {
     if (Lexer.isNot(AsmToken::Identifier))
       return true;
-    auto &Flt = Lexer.getTok();
-    auto S = Flt.getString();
-    double Val;
+    auto S = Lexer.getTok().getString();
     if (S.compare_insensitive("infinity") == 0) {
       Val = std::numeric_limits<double>::infinity();
     } else if (S.compare_insensitive("nan") == 0) {
@@ -498,12 +591,20 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
     }
     if (IsNegative)
       Val = -Val;
-    Operands.push_back(std::make_unique<WebAssemblyOperand>(
-        Flt.getLoc(), Flt.getEndLoc(), WebAssemblyOperand::FltOp{Val}));
     Parser.Lex();
     return false;
   }
 
+  bool parseSpecialFloatMaybe(bool IsNegative, OperandVector &Operands) {
+    SMLoc Start = Lexer.getTok().getLoc(), End = Lexer.getTok().getEndLoc();
+    double Val;
+    if (parseSpecialFloatMaybe(IsNegative, Val))
+      return true;
+    Operands.push_back(std::make_unique<WebAssemblyOperand>(
+        Start, End, WebAssemblyOperand::FltOp{Val}));
+    return false;
+  }
+
   bool addMemOrderOrDefault(OperandVector &Operands) {
     auto &Tok = Lexer.getTok();
     int64_t Order = wasm::WASM_MEM_ORDER_SEQ_CST;
@@ -1004,6 +1105,7 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
     auto &Ctx = Out.getContext();
 
     if (DirectiveID.getString() == ".globaltype") {
+      auto SymNameTok = Lexer.getTok();
       auto SymName = expectIdent();
       if (SymName.empty())
         return ParseStatus::Failure;
@@ -1034,6 +1136,15 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
       // Now set this symbol with the correct type.
       auto *WasmSym =
           static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(SymName));
+      if (WasmSym->hasGlobalInitExpr()) {
+        std::optional<wasm::WasmInitExpr> Expected =
+            WebAssembly::makeGlobalInitExpr(*Type, 0);
+        if (!Expected ||
+            Expected->Inst.Opcode != WasmSym->getGlobalInitExpr().Inst.Opcode)
+          return error(
+              "Type does not match the .globalinit already given for: ",
+              SymNameTok);
+      }
       WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
       WasmSym->setGlobalType(wasm::WasmGlobalType{uint8_t(*Type), Mutable});
       // And emit the directive again.
@@ -1041,6 +1152,36 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
       return expect(AsmToken::EndOfStatement, "EOL");
     }
 
+    if (DirectiveID.getString() == ".globalinit") {
+      // .globalinit SYM, <type>.const VALUE
+      auto SymNameTok = Lexer.getTok();
+      auto SymName = expectIdent();
+      if (SymName.empty())
+        return ParseStatus::Failure;
+      if (expect(AsmToken::Comma, ","))
+        return ParseStatus::Failure;
+      auto *WasmSym =
+          static_cast<MCSymbolWasm *>(Ctx.getOrCreateSymbol(SymName));
+      if (WasmSym->getType() && !WasmSym->isGlobal())
+        return error(".globalinit is only valid for a global: ", SymNameTok);
+      if (!WasmSym->isGlobal() || !WasmSym->hasGlobalType())
+        return error(".globalinit requires a preceding .globaltype for: ",
+                     SymNameTok);
+      std::optional<wasm::WasmInitExpr> InitExpr;
+      if (parseGlobalInit(
+              static_cast<wasm::ValType>(WasmSym->getGlobalType().Type),
+              InitExpr))
+        return ParseStatus::Failure;
+      // An .include'd header may repeat the same .globalinit.
+      if (WasmSym->hasGlobalInitExpr() &&
+          !sameInitExpr(WasmSym->getGlobalInitExpr().Inst, InitExpr->Inst))
+        return error("Conflicting .globalinit for: ", SymNameTok);
+      WasmSym->setGlobalInitExpr(*InitExpr);
+      GlobalInitLocs.try_emplace(WasmSym, SymNameTok.getLoc());
+      TOut.emitGlobalInit(WasmSym);
+      return expect(AsmToken::EndOfStatement, "EOL");
+    }
+
     if (DirectiveID.getString() == ".tabletype") {
       // .tabletype SYM, ELEMTYPE[, MINSIZE[, MAXSIZE]]
       auto SymName = expectIdent();
@@ -1384,7 +1525,14 @@ class WebAssemblyAsmParser final : public 
MCTargetAsmParser {
     TC.clear();
   }
 
-  void onEndOfFile() override { ensureEmptyNestingStack(); }
+  void onEndOfFile() override {
+    ensureEmptyNestingStack();
+    // A later directive can re-declare the symbol as another kind, including
+    // .type, which this parser does not handle.
+    for (auto &[Sym, Loc] : GlobalInitLocs)
+      if (!Sym->isGlobal())
+        error(".globalinit is only valid for a global: " + Sym->getName(), 
Loc);
+  }
 };
 } // end anonymous namespace
 
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
index 5bfaffba3e578..5a74d13641c02 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
@@ -302,30 +302,6 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, 
uint64_t Address,
   }
 }
 
-static std::string toString(const APFloat &FP) {
-  // Print NaNs with custom payloads specially.
-  if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
-      !FP.bitwiseIsEqual(
-          APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
-    APInt AI = FP.bitcastToAPInt();
-    return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
-           utohexstr(AI.getZExtValue() &
-                         (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
-                                                 : 
INT64_C(0x000fffffffffffff)),
-                     /*LowerCase=*/true);
-  }
-
-  // Use C99's hexadecimal floating-point representation.
-  static const size_t BufBytes = 128;
-  char Buf[BufBytes];
-  auto Written = FP.convertToHexString(
-      Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
-  (void)Written;
-  assert(Written != 0);
-  assert(Written < BufBytes);
-  return Buf;
-}
-
 void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                           const MCSubtargetInfo &STI,
                                           raw_ostream &O, bool IsVariadicDef) {
@@ -347,9 +323,11 @@ void WebAssemblyInstPrinter::printOperand(const MCInst 
*MI, unsigned OpNo,
   } else if (Op.isImm()) {
     O << Op.getImm();
   } else if (Op.isSFPImm()) {
-    O << ::toString(APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));
+    O << WebAssembly::floatToString(
+        APFloat(APFloat::IEEEsingle(), APInt(32, Op.getSFPImm())));
   } else if (Op.isDFPImm()) {
-    O << ::toString(APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));
+    O << WebAssembly::floatToString(
+        APFloat(APFloat::IEEEdouble(), APInt(64, Op.getDFPImm())));
   } else {
     assert(Op.isExpr() && "unknown operand kind in printOperand");
     // call_indirect instructions have a TYPEINDEX operand that we print
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp
index 6e99fc3b9b9fc..d187f8a9e7651 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.cpp
@@ -13,6 +13,9 @@
 
 #include "WebAssemblyMCTypeUtilities.h"
 #include "WebAssemblyMCTargetDesc.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace llvm;
@@ -120,3 +123,53 @@ wasm::ValType WebAssembly::regClassToValType(unsigned RC) {
     llvm_unreachable("unexpected type");
   }
 }
+
+std::optional<wasm::WasmInitExpr>
+WebAssembly::makeGlobalInitExpr(wasm::ValType Type, uint64_t Bits) {
+  wasm::WasmInitExpr Expr;
+  Expr.Extended = false;
+  switch (Type) {
+  case wasm::ValType::I32:
+    Expr.Inst.Opcode = wasm::WASM_OPCODE_I32_CONST;
+    Expr.Inst.Value.Int32 = static_cast<int32_t>(static_cast<uint32_t>(Bits));
+    return Expr;
+  case wasm::ValType::I64:
+    Expr.Inst.Opcode = wasm::WASM_OPCODE_I64_CONST;
+    Expr.Inst.Value.Int64 = static_cast<int64_t>(Bits);
+    return Expr;
+  case wasm::ValType::F32:
+    Expr.Inst.Opcode = wasm::WASM_OPCODE_F32_CONST;
+    Expr.Inst.Value.Float32 = static_cast<uint32_t>(Bits);
+    return Expr;
+  case wasm::ValType::F64:
+    Expr.Inst.Opcode = wasm::WASM_OPCODE_F64_CONST;
+    Expr.Inst.Value.Float64 = Bits;
+    return Expr;
+  default:
+    return std::nullopt;
+  }
+}
+
+std::string WebAssembly::floatToString(const APFloat &FP) {
+  // Print NaNs with custom payloads specially.
+  if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
+      !FP.bitwiseIsEqual(
+          APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
+    APInt AI = FP.bitcastToAPInt();
+    return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
+           utohexstr(AI.getZExtValue() &
+                         (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
+                                                 : 
INT64_C(0x000fffffffffffff)),
+                     /*LowerCase=*/true);
+  }
+
+  // Use C99's hexadecimal floating-point representation.
+  static const size_t BufBytes = 128;
+  char Buf[BufBytes];
+  auto Written = FP.convertToHexString(
+      Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
+  (void)Written;
+  assert(Written != 0);
+  assert(Written < BufBytes);
+  return Buf;
+}
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h
index aa630c7ddd6ad..6ac811c182f46 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTypeUtilities.h
@@ -19,6 +19,8 @@
 
 namespace llvm {
 
+class APFloat;
+
 namespace WebAssembly {
 
 /// Used as immediate MachineOperands for block signatures
@@ -50,6 +52,11 @@ inline bool isRefType(wasm::ValType Type) {
          Type == wasm::ValType::EXNREF;
 }
 
+inline bool isNumType(wasm::ValType Type) {
+  return Type == wasm::ValType::I32 || Type == wasm::ValType::I64 ||
+         Type == wasm::ValType::F32 || Type == wasm::ValType::F64;
+}
+
 // Convert ValType or a list/signature of ValTypes to a string.
 
 // Convert an unsigned integer, which can be among wasm::ValType enum, to its
@@ -73,6 +80,15 @@ wasm::ValType regClassToValType(unsigned RC);
 std::optional<wasm::ValType> parseType(StringRef Type);
 BlockType parseBlockType(StringRef Type);
 
+// Build the `<type>.const` init expression that gives a global of type Type 
the
+// value whose raw bits are Bits, or nullopt if Type is not a number type.
+std::optional<wasm::WasmInitExpr> makeGlobalInitExpr(wasm::ValType Type,
+                                                     uint64_t Bits);
+
+// Spell a float the way the WebAssembly text format does: a C99 hexadecimal
+// floating-point literal, or nan:0x<payload> for a non-canonical NaN.
+std::string floatToString(const APFloat &FP);
+
 } // end namespace WebAssembly
 } // end namespace llvm
 
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index dba974d60e5bc..6eeeba715cf61 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -15,6 +15,8 @@
 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
 #include "MCTargetDesc/WebAssemblyMCAsmInfo.h"
 #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCSectionWasm.h"
 #include "llvm/MC/MCSymbolWasm.h"
@@ -73,6 +75,35 @@ void WebAssemblyTargetAsmStreamer::emitGlobalType(const 
MCSymbolWasm *Sym) {
   OS << '\n';
 }
 
+void WebAssemblyTargetAsmStreamer::emitGlobalInit(const MCSymbolWasm *Sym) {
+  assert(Sym->isGlobal());
+  if (!Sym->hasGlobalInitExpr())
+    return;
+  const wasm::WasmInitExprMVP &Inst = Sym->getGlobalInitExpr().Inst;
+  OS << "\t.globalinit\t" << Sym->getName() << ", ";
+  switch (Inst.Opcode) {
+  case wasm::WASM_OPCODE_I32_CONST:
+    OS << "i32.const " << Inst.Value.Int32;
+    break;
+  case wasm::WASM_OPCODE_I64_CONST:
+    OS << "i64.const " << Inst.Value.Int64;
+    break;
+  case wasm::WASM_OPCODE_F32_CONST:
+    OS << "f32.const "
+       << WebAssembly::floatToString(
+              APFloat(APFloat::IEEEsingle(), APInt(32, Inst.Value.Float32)));
+    break;
+  case wasm::WASM_OPCODE_F64_CONST:
+    OS << "f64.const "
+       << WebAssembly::floatToString(
+              APFloat(APFloat::IEEEdouble(), APInt(64, Inst.Value.Float64)));
+    break;
+  default:
+    llvm_unreachable("only number types carry an init expression");
+  }
+  OS << '\n';
+}
+
 void WebAssemblyTargetAsmStreamer::emitTableType(const MCSymbolWasm *Sym) {
   assert(Sym->isTable());
   const wasm::WasmTableType &Type = Sym->getTableType();
diff --git 
a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h 
b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
index d1eb6f67176f2..da2f7e100738d 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h
@@ -38,6 +38,8 @@ class WebAssemblyTargetStreamer : public MCTargetStreamer {
   virtual void emitIndIdx(const MCExpr *Value) = 0;
   /// .globaltype
   virtual void emitGlobalType(const MCSymbolWasm *Sym) = 0;
+  /// .globalinit
+  virtual void emitGlobalInit(const MCSymbolWasm *Sym) = 0;
   /// .tabletype
   virtual void emitTableType(const MCSymbolWasm *Sym) = 0;
   /// .tagtype
@@ -67,6 +69,7 @@ class WebAssemblyTargetAsmStreamer final : public 
WebAssemblyTargetStreamer {
   void emitFunctionType(const MCSymbolWasm *Sym) override;
   void emitIndIdx(const MCExpr *Value) override;
   void emitGlobalType(const MCSymbolWasm *Sym) override;
+  void emitGlobalInit(const MCSymbolWasm *Sym) override;
   void emitTableType(const MCSymbolWasm *Sym) override;
   void emitTagType(const MCSymbolWasm *Sym) override;
   void emitImportModule(const MCSymbolWasm *Sym, StringRef ImportModule) 
override;
@@ -83,6 +86,7 @@ class WebAssemblyTargetWasmStreamer final : public 
WebAssemblyTargetStreamer {
   void emitFunctionType(const MCSymbolWasm *Sym) override {}
   void emitIndIdx(const MCExpr *Value) override;
   void emitGlobalType(const MCSymbolWasm *Sym) override {}
+  void emitGlobalInit(const MCSymbolWasm *Sym) override {}
   void emitTableType(const MCSymbolWasm *Sym) override {}
   void emitTagType(const MCSymbolWasm *Sym) override {}
   void emitImportModule(const MCSymbolWasm *Sym,
@@ -103,6 +107,7 @@ class WebAssemblyTargetNullStreamer final : public 
WebAssemblyTargetStreamer {
   void emitFunctionType(const MCSymbolWasm *) override {}
   void emitIndIdx(const MCExpr *) override {}
   void emitGlobalType(const MCSymbolWasm *) override {}
+  void emitGlobalInit(const MCSymbolWasm *) override {}
   void emitTableType(const MCSymbolWasm *) override {}
   void emitTagType(const MCSymbolWasm *) override {}
   void emitImportModule(const MCSymbolWasm *, StringRef) override {}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp 
b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 26e224a597540..4c1c610ec5398 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -190,24 +190,41 @@ MCSymbolWasm 
*WebAssemblyAsmPrinter::getMCSymbolForFunction(
   return WasmSym;
 }
 
-static std::optional<uint64_t> getWasmGlobalInitBits(const Constant *C) {
-  // Sub-i32 ints and half/bfloat legalize to a single i32/f32 MVT and so pass
-  // wasmSymbolSetType(), but their bits are not an i32/f32 init value; gate on
-  // the exact IR type so they fall back to the default 0 rather than emit a
-  // garbage init expr.
-  if (const auto *CI = dyn_cast<ConstantInt>(C)) {
-    unsigned BW = CI->getType()->getIntegerBitWidth();
-    if (BW == 32 || BW == 64)
-      return CI->getValue().getZExtValue();
-    return std::nullopt;
+static std::optional<wasm::WasmInitExpr>
+getWasmGlobalInitExpr(const Constant *C, wasm::ValType Type) {
+  // A single-element aggregate legalizes to its element's type.
+  while (C->getType()->isAggregateType() || C->getType()->isVectorTy()) {
+    if (C->getAggregateElement(1u))
+      return std::nullopt;
+    C = C->getAggregateElement(0u);
+    if (!C)
+      return std::nullopt;
+  }
+  switch (Type) {
+  case wasm::ValType::I32:
+  case wasm::ValType::I64: {
+    // i1, i8, i16 and half also legalize to i32, but loads and stores of such
+    // a global do not use global.get or global.set.
+    const auto *CI = dyn_cast<ConstantInt>(C);
+    unsigned Width = Type == wasm::ValType::I32 ? 32 : 64;
+    if (!CI || CI->getBitWidth() != Width)
+      return std::nullopt;
+    return WebAssembly::makeGlobalInitExpr(Type, 
CI->getValue().getZExtValue());
+  }
+  case wasm::ValType::F32:
+  case wasm::ValType::F64: {
+    // bfloat also legalizes to f32, but loads and stores of such a global
+    // fail instruction selection.
+    const auto *CF = dyn_cast<ConstantFP>(C);
+    if (!CF || (Type == wasm::ValType::F32 ? !CF->getType()->isFloatTy()
+                                           : !CF->getType()->isDoubleTy()))
+      return std::nullopt;
+    return WebAssembly::makeGlobalInitExpr(
+        Type, CF->getValueAPF().bitcastToAPInt().getZExtValue());
   }
-  if (const auto *CF = dyn_cast<ConstantFP>(C)) {
-    Type *T = CF->getType();
-    if (T->isFloatTy() || T->isDoubleTy())
-      return CF->getValueAPF().bitcastToAPInt().getZExtValue();
+  default:
     return std::nullopt;
   }
-  return std::nullopt;
 }
 
 void WebAssemblyAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
@@ -258,12 +275,24 @@ void WebAssemblyAsmPrinter::emitGlobalVariable(const 
GlobalVariable *GV) {
 
   emitVisibility(Sym, GV->getVisibility(), !GV->isDeclaration());
 
-  // WasmObjectWriter reads these init bits back off the symbol to emit the 
init
-  // expression instead of the type's default 0.
   if (GV->hasInitializer() && Sym->isGlobal()) {
-    if (std::optional<uint64_t> Bits =
-            getWasmGlobalInitBits(GV->getInitializer()))
-      Sym->setGlobalInitValue(*Bits);
+    const Constant *Init = GV->getInitializer();
+    auto Type = static_cast<wasm::ValType>(Sym->getGlobalType().Type);
+    // The object writer gives reference types ref.null and rejects v128.
+    // undef, poison and zero take the type's default.
+    if (WebAssembly::isNumType(Type) && !isa<UndefValue>(Init) &&
+        !Init->isNullValue()) {
+      if (std::optional<wasm::WasmInitExpr> InitExpr =
+              getWasmGlobalInitExpr(Init, Type)) {
+        Sym->setGlobalInitExpr(*InitExpr);
+      } else {
+        // Keep emitting the declaration so the printed assembly stays well
+        // formed. The error discards the object.
+        OutContext.reportError(SMLoc(), "unsupported initializer for "
+                                        "WebAssembly global '" +
+                                            Sym->getName() + "'");
+      }
+    }
   }
 
   emitSymbolType(Sym);
@@ -366,6 +395,7 @@ void WebAssemblyAsmPrinter::emitSymbolType(const 
MCSymbolWasm *Sym) {
   switch (*WasmTy) {
   case wasm::WASM_SYMBOL_TYPE_GLOBAL:
     getTargetStreamer()->emitGlobalType(Sym);
+    getTargetStreamer()->emitGlobalInit(Sym);
     break;
   case wasm::WASM_SYMBOL_TYPE_TAG:
     getTargetStreamer()->emitTagType(Sym);
diff --git a/llvm/test/CodeGen/WebAssembly/global-addrspace.ll 
b/llvm/test/CodeGen/WebAssembly/global-addrspace.ll
index d6f0439642a68..9c2bd7677c5a7 100644
--- a/llvm/test/CodeGen/WebAssembly/global-addrspace.ll
+++ b/llvm/test/CodeGen/WebAssembly/global-addrspace.ll
@@ -5,26 +5,31 @@
 ;; (e.g., in modules without functions) and correctly emit their initializers.
 
 ; CHECK: .globaltype wasm_var, i32
+; CHECK-NEXT: .globalinit wasm_var, i32.const 42
 ; CHECK: .globl wasm_var
 ; CHECK-LABEL: wasm_var:
 @wasm_var = addrspace(1) global i32 42
 
 ; CHECK: .globaltype wasm_var_float, f32
+; CHECK-NEXT: .globalinit wasm_var_float, f32.const 0x1.91eb86p1
 ; CHECK: .globl wasm_var_float
 ; CHECK-LABEL: wasm_var_float:
 @wasm_var_float = addrspace(1) global float 0x40091EB860000000
 
 ; CHECK: .globaltype     wasm_var_i64, i64
+; CHECK-NEXT: .globalinit wasm_var_i64, i64.const 1234567890
 ; CHECK: .globl  wasm_var_i64
 ; CHECK-LABEL: wasm_var_i64:
 @wasm_var_i64 = addrspace(1) global i64 1234567890
 
 ; CHECK: .globaltype     wasm_var_f64, f64
+; CHECK-NEXT: .globalinit wasm_var_f64, f64.const -0x0p0
 ; CHECK: .globl  wasm_var_f64
 ; CHECK-LABEL: wasm_var_f64:
 @wasm_var_f64 = local_unnamed_addr addrspace(1) global double -0.0
 
 ; CHECK: .globaltype wasm_external, i32
 ; CHECK-NOT: .global wasm_external
+; CHECK-NOT: .globalinit wasm_external
 ; CHECK-NOT: wasm_external:
 @wasm_external = external addrspace(1) global i32
diff --git a/llvm/test/CodeGen/WebAssembly/global-init-invalid.ll 
b/llvm/test/CodeGen/WebAssembly/global-init-invalid.ll
new file mode 100644
index 0000000000000..140fb80b84144
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-init-invalid.ll
@@ -0,0 +1,35 @@
+; RUN: not llc < %s -mtriple=wasm32-unknown-unknown -filetype=obj -o /dev/null 
2>&1 | FileCheck %s
+; RUN: not llc < %s -mtriple=wasm32-unknown-unknown -filetype=asm -o - 2>&1 | 
FileCheck %s --check-prefix=ASM
+
+; Initializers that a Wasm init expression cannot represent are diagnosed.
+
+; The declaration is still printed after the diagnostic.
+; ASM: error: unsupported initializer for WebAssembly global 'narrow_int'
+; ASM: .globl narrow_int
+; ASM: narrow_int:
+
+; CHECK: error: unsupported initializer for WebAssembly global 'narrow_int'
+@narrow_int = addrspace(1) global i8 5
+
+; CHECK: error: unsupported initializer for WebAssembly global 'narrow_bool'
+@narrow_bool = addrspace(1) global i1 true
+
+; CHECK: error: unsupported initializer for WebAssembly global 'short_int'
+@short_int = addrspace(1) global i16 4660
+
+; CHECK: error: unsupported initializer for WebAssembly global 'half_fp'
+@half_fp = addrspace(1) global half 2.0
+
+; CHECK: error: unsupported initializer for WebAssembly global 'bfloat_fp'
+@bfloat_fp = addrspace(1) global bfloat 2.0
+
+@data = global i32 7
+; CHECK: error: unsupported initializer for WebAssembly global 'sym_addr'
+@sym_addr = addrspace(1) global ptr @data
+
+; CHECK: error: unsupported initializer for WebAssembly global 'sym_int'
+@sym_int = addrspace(1) global i32 ptrtoint (ptr @data to i32)
+
+@arr = global [4 x i32] zeroinitializer
+; CHECK: error: unsupported initializer for WebAssembly global 'sym_offset'
+@sym_offset = addrspace(1) global ptr getelementptr inbounds ([4 x i32], ptr 
@arr, i32 0, i32 2)
diff --git a/llvm/test/CodeGen/WebAssembly/global-init-reftype.ll 
b/llvm/test/CodeGen/WebAssembly/global-init-reftype.ll
new file mode 100644
index 0000000000000..a9505cb2767d6
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-init-reftype.ll
@@ -0,0 +1,22 @@
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -mattr=+reference-types 
-filetype=obj | obj2yaml | FileCheck %s --implicit-check-not=error:
+
+; undef and zeroinitializer both give an externref global ref.null, with no
+; diagnostic.
+
+@undefref = addrspace(1) global target("wasm.externref") undef
+@nullref = addrspace(1) global target("wasm.externref") zeroinitializer
+
+; CHECK:      - Type:            GLOBAL
+; CHECK-NEXT:   Globals:
+; CHECK-NEXT:     - Index:           0
+; CHECK-NEXT:       Type:            EXTERNREF
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          REF_NULL
+; CHECK-NEXT:         Type:            EXTERNREF
+; CHECK-NEXT:     - Index:           1
+; CHECK-NEXT:       Type:            EXTERNREF
+; CHECK-NEXT:       Mutable:         true
+; CHECK-NEXT:       InitExpr:
+; CHECK-NEXT:         Opcode:          REF_NULL
+; CHECK-NEXT:         Type:            EXTERNREF
diff --git a/llvm/test/CodeGen/WebAssembly/global-init.ll 
b/llvm/test/CodeGen/WebAssembly/global-init.ll
index 72e5c180c649c..a0da7ff68e4d4 100644
--- a/llvm/test/CodeGen/WebAssembly/global-init.ll
+++ b/llvm/test/CodeGen/WebAssembly/global-init.ll
@@ -1,8 +1,10 @@
-; RUN: llc < %s -mtriple=wasm32-unknown-unknown -filetype=obj | obj2yaml | 
FileCheck %s
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown -filetype=obj | obj2yaml | 
FileCheck %s --check-prefix=OBJ --implicit-check-not=error:
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown | llvm-mc 
-triple=wasm32-unknown-unknown -filetype=obj | obj2yaml | FileCheck %s 
--check-prefix=OBJ
+; RUN: llc < %s -mtriple=wasm32-unknown-unknown | FileCheck %s 
--check-prefix=ASM
 
-; No function is defined here, so the AsmPrinter's Subtarget is null: emitting
-; these address-space-1 globals must not crash, and each initializer must reach
-; the Global section instead of the type's default 0.
+; No function is defined here, so the AsmPrinter has no Subtarget. Emitting
+; these globals must not crash, and each initializer must reach the Global
+; section.
 
 @i32v = addrspace(1) global i32 33554435
 @i64v = addrspace(1) global i64 8589934595
@@ -10,35 +12,124 @@
 @f64v = addrspace(1) global double 2.0
 @constv = addrspace(1) constant i32 42
 
-; CHECK:      - Type:            GLOBAL
-; CHECK-NEXT:   Globals:
-; CHECK-NEXT:     - Index:           0
-; CHECK-NEXT:       Type:            I32
-; CHECK-NEXT:       Mutable:         true
-; CHECK-NEXT:       InitExpr:
-; CHECK-NEXT:         Opcode:          I32_CONST
-; CHECK-NEXT:         Value:           33554435
-; CHECK-NEXT:     - Index:           1
-; CHECK-NEXT:       Type:            I64
-; CHECK-NEXT:       Mutable:         true
-; CHECK-NEXT:       InitExpr:
-; CHECK-NEXT:         Opcode:          I64_CONST
-; CHECK-NEXT:         Value:           8589934595
-; CHECK-NEXT:     - Index:           2
-; CHECK-NEXT:       Type:            F32
-; CHECK-NEXT:       Mutable:         true
-; CHECK-NEXT:       InitExpr:
-; CHECK-NEXT:         Opcode:          F32_CONST
-; CHECK-NEXT:         Value:           1073741824
-; CHECK-NEXT:     - Index:           3
-; CHECK-NEXT:       Type:            F64
-; CHECK-NEXT:       Mutable:         true
-; CHECK-NEXT:       InitExpr:
-; CHECK-NEXT:         Opcode:          F64_CONST
-; CHECK-NEXT:         Value:           4611686018427387904
-; CHECK-NEXT:     - Index:           4
-; CHECK-NEXT:       Type:            I32
-; CHECK-NEXT:       Mutable:         false
-; CHECK-NEXT:       InitExpr:
-; CHECK-NEXT:         Opcode:          I32_CONST
-; CHECK-NEXT:         Value:           42
+; undef, poison and zero keep the type's default and are not diagnosed.
+@undefv = addrspace(1) global i32 undef
+@poisonv = addrspace(1) global i32 poison
+@nullv = addrspace(1) global ptr null
+@zagg = addrspace(1) global {i32} zeroinitializer
+@znarrow = addrspace(1) global i8 0
+
+; A single-element aggregate holds its element's value.
+@agg = addrspace(1) global {i32} {i32 5}
+@arr = addrspace(1) global [1 x i64] [i64 6]
+@vec = addrspace(1) global <1 x float> <float 2.0>
+@nest = addrspace(1) global {[1 x double]} {[1 x double] [double 2.0]}
+
+; ASM:      .globaltype i32v, i32
+; ASM-NEXT: .globalinit i32v, i32.const 33554435
+; ASM:      .globaltype i64v, i64
+; ASM-NEXT: .globalinit i64v, i64.const 8589934595
+; ASM:      .globaltype f32v, f32
+; ASM-NEXT: .globalinit f32v, f32.const 0x1p1
+; ASM:      .globaltype f64v, f64
+; ASM-NEXT: .globalinit f64v, f64.const 0x1p1
+; ASM:      .globaltype constv, i32, immutable
+; ASM-NEXT: .globalinit constv, i32.const 42
+; A default value needs no .globalinit line.
+; ASM:      .globaltype undefv, i32
+; ASM-NOT:  .globalinit undefv
+; ASM:      .globaltype agg, i32
+; ASM-NEXT: .globalinit agg, i32.const 5
+; ASM:      .globaltype arr, i64
+; ASM-NEXT: .globalinit arr, i64.const 6
+; ASM:      .globaltype vec, f32
+; ASM-NEXT: .globalinit vec, f32.const 0x1p1
+; ASM:      .globaltype nest, f64
+; ASM-NEXT: .globalinit nest, f64.const 0x1p1
+
+; OBJ:      - Type:            GLOBAL
+; OBJ-NEXT:   Globals:
+; OBJ-NEXT:     - Index:           0
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           33554435
+; OBJ-NEXT:     - Index:           1
+; OBJ-NEXT:       Type:            I64
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I64_CONST
+; OBJ-NEXT:         Value:           8589934595
+; OBJ-NEXT:     - Index:           2
+; OBJ-NEXT:       Type:            F32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          F32_CONST
+; OBJ-NEXT:         Value:           1073741824
+; OBJ-NEXT:     - Index:           3
+; OBJ-NEXT:       Type:            F64
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          F64_CONST
+; OBJ-NEXT:         Value:           4611686018427387904
+; OBJ-NEXT:     - Index:           4
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         false
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           42
+; OBJ-NEXT:     - Index:           5
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           0
+; OBJ-NEXT:     - Index:           6
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           0
+; OBJ-NEXT:     - Index:           7
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           0
+; OBJ-NEXT:     - Index:           8
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           0
+; OBJ-NEXT:     - Index:           9
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           0
+; OBJ-NEXT:     - Index:           10
+; OBJ-NEXT:       Type:            I32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I32_CONST
+; OBJ-NEXT:         Value:           5
+; OBJ-NEXT:     - Index:           11
+; OBJ-NEXT:       Type:            I64
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          I64_CONST
+; OBJ-NEXT:         Value:           6
+; OBJ-NEXT:     - Index:           12
+; OBJ-NEXT:       Type:            F32
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          F32_CONST
+; OBJ-NEXT:         Value:           1073741824
+; OBJ-NEXT:     - Index:           13
+; OBJ-NEXT:       Type:            F64
+; OBJ-NEXT:       Mutable:         true
+; OBJ-NEXT:       InitExpr:
+; OBJ-NEXT:         Opcode:          F64_CONST
+; OBJ-NEXT:         Value:           4611686018427387904
diff --git a/llvm/test/CodeGen/WebAssembly/global-unsupported-type.ll 
b/llvm/test/CodeGen/WebAssembly/global-unsupported-type.ll
new file mode 100644
index 0000000000000..29a16a2573c4f
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-unsupported-type.ll
@@ -0,0 +1,12 @@
+; RUN: split-file %s %t
+; RUN: not llc < %t/vec.ll -mtriple=wasm32-unknown-unknown -mattr=+simd128 
-filetype=obj -o /dev/null 2>&1 | FileCheck %s --check-prefix=V128
+; RUN: not llc < %t/fref.ll -mtriple=wasm32-unknown-unknown 
-mattr=+reference-types -filetype=obj -o /dev/null 2>&1 | FileCheck %s 
--check-prefix=FUNCREF
+
+; V128: error: WebAssembly global 'vec' has a type that cannot be initialized
+; FUNCREF: error: WebAssembly global 'fref' has a type that cannot be 
initialized
+
+;--- vec.ll
+@vec = addrspace(1) global <4 x i32> zeroinitializer
+
+;--- fref.ll
+@fref = addrspace(1) global target("wasm.funcref") zeroinitializer
diff --git a/llvm/test/MC/WebAssembly/globalinit-invalid.s 
b/llvm/test/MC/WebAssembly/globalinit-invalid.s
new file mode 100644
index 0000000000000..13377388da63e
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globalinit-invalid.s
@@ -0,0 +1,77 @@
+# RUN: not llvm-mc -triple=wasm32 %s 2>&1 | FileCheck %s
+
+# CHECK: error: .globalinit requires a preceding .globaltype for: nodecl
+.globalinit nodecl, i32.const 1
+
+# CHECK: error: Expected identifier, got: 42
+.globalinit 42, i32.const 1
+
+# A symbol of another kind cannot take an initial value.
+.functype fn () -> ()
+# CHECK: error: .globalinit is only valid for a global: fn
+.globalinit fn, i32.const 1
+.tabletype tbl, externref
+# CHECK: error: .globalinit is only valid for a global: tbl
+.globalinit tbl, i32.const 1
+.tagtype tag i32
+# CHECK: error: .globalinit is only valid for a global: tag
+.globalinit tag, i32.const 1
+
+.globaltype sym, i32
+# CHECK: error: Expected a const instruction in .globalinit: hello
+.globalinit sym, hello
+# CHECK: error: Initial value does not match the global's type: f32.const
+.globalinit sym, f32.const 1.0
+# CHECK: error: Expected integer constant, instead got:
+.globalinit sym, i32.const
+# CHECK: error: Expected EOL, instead got: ,
+.globalinit sym, i32.const 42,
+
+# CHECK: error: Integer constant does not fit in the global's type: 4294967296
+.globalinit sym, i32.const 4294967296
+# CHECK: :[[@LINE+1]]:28: error: Integer constant does not fit in the global's 
type: -4294967295
+.globalinit sym, i32.const -4294967295
+
+.globaltype i64sym, i64
+# CHECK: error: Integer constant does not fit in the global's type: 
18446744073709551616
+.globalinit i64sym, i64.const 18446744073709551616
+
+.globaltype vec, v128
+# CHECK: error: Initial value is not supported for this global type: v128.const
+.globalinit vec, v128.const 0
+
+.globaltype flt, f32
+# CHECK: error: Expected floating-point constant, instead got: hello
+.globalinit flt, f32.const hello
+# CHECK: error: Expected floating-point constant, instead got: notanumber
+.globalinit flt, f32.const notanumber
+# The value takes the spellings an f32.const operand takes.
+# CHECK: error: Cannot parse real: 0.1
+.globalinit flt, f32.const 0.1
+# CHECK: error: Expected floating-point constant, instead got: inf
+.globalinit flt, f32.const inf
+# CHECK: error: Expected floating-point constant, instead got: 1
+.globalinit flt, f32.const 1
+
+# A later .globaltype may repeat the type the value was given, but not
+# contradict it.
+.globaltype retyped, i32
+.globalinit retyped, i32.const 42
+.globaltype retyped, i32
+# CHECK: error: Type does not match the .globalinit already given for: retyped
+.globaltype retyped, f64
+# CHECK: error: Type does not match the .globalinit already given for: retyped
+.globaltype retyped, v128
+
+# Repeating the same initial value is fine; contradicting it is not.
+.globaltype conflict, i32
+.globalinit conflict, i32.const 1
+.globalinit conflict, i32.const 1
+# CHECK: error: Conflicting .globalinit for: conflict
+.globalinit conflict, i32.const 2
+
+# nan and -nan are different values.
+.globaltype nanconflict, f32
+.globalinit nanconflict, f32.const nan
+# CHECK: error: Conflicting .globalinit for: nanconflict
+.globalinit nanconflict, f32.const -nan
diff --git a/llvm/test/MC/WebAssembly/globalinit-not-global.s 
b/llvm/test/MC/WebAssembly/globalinit-not-global.s
new file mode 100644
index 0000000000000..0fd2e85062869
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globalinit-not-global.s
@@ -0,0 +1,32 @@
+# RUN: not llvm-mc -triple=wasm32-unknown-unknown %s -o /dev/null 2>&1 | 
FileCheck %s
+
+# Re-declaring a global as another kind of symbol would drop its value.
+
+       .globaltype     fn, i32
+# CHECK: :[[@LINE+1]]:14: error: .globalinit is only valid for a global: fn
+       .globalinit     fn, i32.const 1
+       .functype       fn () -> ()
+       .globl  fn
+fn:
+       .functype       fn () -> ()
+       end_function
+
+       .globaltype     obj, i32
+# CHECK: :[[@LINE+1]]:14: error: .globalinit is only valid for a global: obj
+       .globalinit     obj, i32.const 2
+       .type   obj,@object
+       .section        .data.obj,"",@
+       .globl  obj
+obj:
+       .int32  0
+       .size   obj, 4
+
+       .globaltype     tbl, i32
+# CHECK: :[[@LINE+1]]:14: error: .globalinit is only valid for a global: tbl
+       .globalinit     tbl, i32.const 3
+       .tabletype      tbl, externref
+
+       .globaltype     tag, i32
+# CHECK: :[[@LINE+1]]:14: error: .globalinit is only valid for a global: tag
+       .globalinit     tag, i32.const 4
+       .tagtype        tag i32
diff --git a/llvm/test/MC/WebAssembly/globalinit-undefined.s 
b/llvm/test/MC/WebAssembly/globalinit-undefined.s
new file mode 100644
index 0000000000000..5928192e6cd29
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globalinit-undefined.s
@@ -0,0 +1,22 @@
+# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o 
/dev/null 2>&1 | FileCheck %s
+# RUN: llvm-mc -triple=wasm32-unknown-unknown %s | not llvm-mc 
-triple=wasm32-unknown-unknown -filetype=obj -o /dev/null 2>&1 | FileCheck %s
+
+# An undefined global is imported, so the value given here would be lost.
+
+# CHECK: error: WebAssembly global 'g' has an initial value but is not defined
+       .globaltype     g, i32
+       .globalinit     g, i32.const 5
+
+# Also for a value equal to the type's default.
+# CHECK: error: WebAssembly global 'z' has an initial value but is not defined
+       .globaltype     z, i32
+       .globalinit     z, i32.const 0
+
+       .functype       use () -> (i32)
+       .globl  use
+use:
+       .functype       use () -> (i32)
+       global.get      g
+       global.get      z
+       i32.add
+       end_function
diff --git a/llvm/test/MC/WebAssembly/globalinit.s 
b/llvm/test/MC/WebAssembly/globalinit.s
new file mode 100644
index 0000000000000..d96340cd85a22
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globalinit.s
@@ -0,0 +1,274 @@
+# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o %t.o
+# RUN: obj2yaml %t.o | FileCheck %s --check-prefix=OBJ
+# RUN: llvm-mc -triple=wasm32-unknown-unknown %s | llvm-mc 
-triple=wasm32-unknown-unknown -filetype=obj -o - | obj2yaml | FileCheck %s 
--check-prefix=OBJ
+# RUN: llvm-mc -triple=wasm32-unknown-unknown %s | FileCheck %s 
--check-prefix=ASM
+
+       .globaltype     i32g, i32
+       .globalinit     i32g, i32.const 33554435
+       .globl  i32g
+i32g:
+
+       .globaltype     i64g, i64
+       .globalinit     i64g, i64.const -2
+       .globl  i64g
+i64g:
+
+       .globaltype     f32g, f32
+       .globalinit     f32g, f32.const 0x1p1
+       .globl  f32g
+f32g:
+
+       .globaltype     f64g, f64
+       .globalinit     f64g, f64.const -0x0p0
+       .globl  f64g
+f64g:
+
+       .globaltype     nang, f32
+       .globalinit     nang, f32.const nan
+       .globl  nang
+nang:
+
+       .globaltype     constg, i32, immutable
+       .globalinit     constg, i32.const 42
+       .globl  constg
+constg:
+
+# Either spelling of a value that fits the type's width is accepted.
+
+       .globaltype     u32max, i32
+       .globalinit     u32max, i32.const 4294967295
+       .globl  u32max
+u32max:
+
+       .globaltype     i32min, i32
+       .globalinit     i32min, i32.const -2147483648
+       .globl  i32min
+i32min:
+
+       .globaltype     i32minu, i32
+       .globalinit     i32minu, i32.const 2147483648
+       .globl  i32minu
+i32minu:
+
+       .globaltype     u64max, i64
+       .globalinit     u64max, i64.const 18446744073709551615
+       .globl  u64max
+u64max:
+
+# Repeating a declaration is idempotent, both for the type and for the value.
+
+       .globaltype     retyped, i32
+       .globalinit     retyped, i32.const 7
+       .globaltype     retyped, i32
+       .globl  retyped
+retyped:
+
+       .globaltype     twice, i32
+       .globalinit     twice, i32.const 9
+       .globalinit     twice, i32.const 9
+       .globl  twice
+twice:
+
+# A repeat is compared on the union member its opcode selects, so a wider value
+# written for another symbol in between cannot make it look like a conflict.
+
+       .globaltype     wide, f64
+       .globalinit     wide, f64.const 0x1.23456789abcdep100
+       .globl  wide
+wide:
+
+       .globaltype     narrow, i32
+       .globalinit     narrow, i32.const 3
+       .globalinit     wide, f64.const 0x1.23456789abcdep100
+       .globalinit     narrow, i32.const 3
+       .globl  narrow
+narrow:
+
+       .globaltype     inf32, f32
+       .globalinit     inf32, f32.const infinity
+       .globl  inf32
+inf32:
+
+       .globaltype     dec32, f32
+       .globalinit     dec32, f32.const 0.5
+       .globl  dec32
+dec32:
+
+# A negative NaN keeps its sign.
+
+       .globaltype     nneg64, f64
+       .globalinit     nneg64, f64.const -nan
+       .globl  nneg64
+nneg64:
+
+       .globaltype     nneg32, f32
+       .globalinit     nneg32, f32.const -nan
+       .globl  nneg32
+nneg32:
+
+# A value equal to the type's default is printed back.
+
+       .globaltype     zerog, i32
+       .globalinit     zerog, i32.const 0
+       .globl  zerog
+zerog:
+
+# ASM:      .globaltype i32g, i32
+# ASM-NEXT: .globalinit i32g, i32.const 33554435
+# ASM:      .globaltype i64g, i64
+# ASM-NEXT: .globalinit i64g, i64.const -2
+# ASM:      .globaltype f32g, f32
+# ASM-NEXT: .globalinit f32g, f32.const 0x1p1
+# ASM:      .globaltype f64g, f64
+# ASM-NEXT: .globalinit f64g, f64.const -0x0p0
+# ASM:      .globaltype nang, f32
+# ASM-NEXT: .globalinit nang, f32.const nan
+# ASM:      .globaltype constg, i32, immutable
+# ASM-NEXT: .globalinit constg, i32.const 42
+# ASM:      .globaltype u32max, i32
+# ASM-NEXT: .globalinit u32max, i32.const -1
+# ASM:      .globaltype i32min, i32
+# ASM-NEXT: .globalinit i32min, i32.const -2147483648
+# ASM:      .globaltype i32minu, i32
+# ASM-NEXT: .globalinit i32minu, i32.const -2147483648
+# ASM:      .globaltype u64max, i64
+# ASM-NEXT: .globalinit u64max, i64.const -1
+# ASM:      .globaltype retyped, i32
+# ASM-NEXT: .globalinit retyped, i32.const 7
+# ASM-NEXT: .globaltype retyped, i32
+# ASM:      .globaltype twice, i32
+# ASM-NEXT: .globalinit twice, i32.const 9
+# ASM-NEXT: .globalinit twice, i32.const 9
+# ASM:      .globaltype wide, f64
+# ASM-NEXT: .globalinit wide, f64.const 0x1.23456789abcdep100
+# ASM:      .globaltype narrow, i32
+# ASM-NEXT: .globalinit narrow, i32.const 3
+# ASM-NEXT: .globalinit wide, f64.const 0x1.23456789abcdep100
+# ASM-NEXT: .globalinit narrow, i32.const 3
+# ASM:      .globaltype inf32, f32
+# ASM-NEXT: .globalinit inf32, f32.const infinity
+# ASM:      .globaltype dec32, f32
+# ASM-NEXT: .globalinit dec32, f32.const 0x1p-1
+# ASM:      .globaltype nneg64, f64
+# ASM-NEXT: .globalinit nneg64, f64.const -nan
+# ASM:      .globaltype nneg32, f32
+# ASM-NEXT: .globalinit nneg32, f32.const -nan
+# ASM:      .globaltype zerog, i32
+# ASM-NEXT: .globalinit zerog, i32.const 0
+
+# OBJ:      - Type:            GLOBAL
+# OBJ-NEXT:   Globals:
+# OBJ-NEXT:     - Index:           0
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           33554435
+# OBJ-NEXT:     - Index:           1
+# OBJ-NEXT:       Type:            I64
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I64_CONST
+# OBJ-NEXT:         Value:           -2
+# OBJ-NEXT:     - Index:           2
+# OBJ-NEXT:       Type:            F32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F32_CONST
+# OBJ-NEXT:         Value:           1073741824
+# OBJ-NEXT:     - Index:           3
+# OBJ-NEXT:       Type:            F64
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F64_CONST
+# OBJ-NEXT:         Value:           9223372036854775808
+# OBJ-NEXT:     - Index:           4
+# OBJ-NEXT:       Type:            F32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F32_CONST
+# OBJ-NEXT:         Value:           2143289344
+# OBJ-NEXT:     - Index:           5
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         false
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           42
+# OBJ-NEXT:     - Index:           6
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           -1
+# OBJ-NEXT:     - Index:           7
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           -2147483648
+# OBJ-NEXT:     - Index:           8
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           -2147483648
+# OBJ-NEXT:     - Index:           9
+# OBJ-NEXT:       Type:            I64
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I64_CONST
+# OBJ-NEXT:         Value:           -1
+# OBJ-NEXT:     - Index:           10
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           7
+# OBJ-NEXT:     - Index:           11
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           9
+# OBJ-NEXT:     - Index:           12
+# OBJ-NEXT:       Type:            F64
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F64_CONST
+# OBJ-NEXT:         Value:           5058162877485726942
+# OBJ-NEXT:     - Index:           13
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           3
+# OBJ-NEXT:     - Index:           14
+# OBJ-NEXT:       Type:            F32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F32_CONST
+# OBJ-NEXT:         Value:           2139095040
+# OBJ-NEXT:     - Index:           15
+# OBJ-NEXT:       Type:            F32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F32_CONST
+# OBJ-NEXT:         Value:           1056964608
+# OBJ-NEXT:     - Index:           16
+# OBJ-NEXT:       Type:            F64
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F64_CONST
+# OBJ-NEXT:         Value:           18444492273895866368
+# OBJ-NEXT:     - Index:           17
+# OBJ-NEXT:       Type:            F32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          F32_CONST
+# OBJ-NEXT:         Value:           4290772992
+# OBJ-NEXT:     - Index:           18
+# OBJ-NEXT:       Type:            I32
+# OBJ-NEXT:       Mutable:         true
+# OBJ-NEXT:       InitExpr:
+# OBJ-NEXT:         Opcode:          I32_CONST
+# OBJ-NEXT:         Value:           0
diff --git a/llvm/test/MC/WebAssembly/globaltype-unsupported.s 
b/llvm/test/MC/WebAssembly/globaltype-unsupported.s
new file mode 100644
index 0000000000000..32f7ba3846f78
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/globaltype-unsupported.s
@@ -0,0 +1,8 @@
+# RUN: not llvm-mc -triple=wasm32-unknown-unknown -filetype=obj %s -o 
/dev/null 2>&1 | FileCheck %s
+
+# The object writer also rejects a v128 global that comes from assembly.
+
+# CHECK: error: WebAssembly global 'g' has a type that cannot be initialized
+       .globaltype     g, v128
+       .globl  g
+g:

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to