[llvm] d7086af - [WebAssembly] Support for WebAssembly globals in LLVM IR

Andy Wingo via llvm-commits llvm-commits at lists.llvm.org
Tue May 11 02:24:15 PDT 2021


Author: Paulo Matos
Date: 2021-05-11T11:19:29+02:00
New Revision: d7086af2143d58a6535e0837c4d8789c69c6985f

URL: https://github.com/llvm/llvm-project/commit/d7086af2143d58a6535e0837c4d8789c69c6985f
DIFF: https://github.com/llvm/llvm-project/commit/d7086af2143d58a6535e0837c4d8789c69c6985f.diff

LOG: [WebAssembly] Support for WebAssembly globals in LLVM IR

This patch adds support for WebAssembly globals in LLVM IR, representing
them as pointers to global values, in a non-default, non-integral
address space.  Instruction selection legalizes loads and stores to
these pointers to new WebAssemblyISD nodes GLOBAL_GET and GLOBAL_SET.
Once the lowering creates the new nodes, tablegen pattern matches those
and converts them to Wasm global.get/set of the appropriate type.

Based on work by Paulo Matos in https://reviews.llvm.org/D95425.

Reviewed By: pmatos

Differential Revision: https://reviews.llvm.org/D101608

Added: 
    llvm/test/CodeGen/WebAssembly/global-get.ll
    llvm/test/CodeGen/WebAssembly/global-set.ll

Modified: 
    clang/lib/Basic/Targets/WebAssembly.h
    clang/test/CodeGen/target-data.c
    llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h
    llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyISD.def
    llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
    llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
    llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
    llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
    llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index be5b66a9580b..70115183e46b 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -147,7 +147,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssembly32TargetInfo
   explicit WebAssembly32TargetInfo(const llvm::Triple &T,
                                    const TargetOptions &Opts)
       : WebAssemblyTargetInfo(T, Opts) {
-    resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128");
+    resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128-ni:1");
   }
 
 protected:
@@ -166,7 +166,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssembly64TargetInfo
     SizeType = UnsignedLong;
     PtrDiffType = SignedLong;
     IntPtrType = SignedLong;
-    resetDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128");
+    resetDataLayout("e-m:e-p:64:64-i64:64-n32:64-S128-ni:1");
   }
 
 protected:

diff  --git a/clang/test/CodeGen/target-data.c b/clang/test/CodeGen/target-data.c
index ea6cef484341..1d88984530e5 100644
--- a/clang/test/CodeGen/target-data.c
+++ b/clang/test/CodeGen/target-data.c
@@ -108,11 +108,11 @@
 
 // RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | \
 // RUN: FileCheck %s -check-prefix=WEBASSEMBLY32
-// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1"
 
 // RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | \
 // RUN: FileCheck %s -check-prefix=WEBASSEMBLY64
-// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128"
+// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1"
 
 // RUN: %clang_cc1 -triple lanai-unknown-unknown -o - -emit-llvm %s | \
 // RUN: FileCheck %s -check-prefix=LANAI

diff  --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h
index 0d9c07cbf1f9..1ec1df5d0c3d 100644
--- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h
+++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyUtilities.h
@@ -28,6 +28,26 @@ class WebAssemblySubtarget;
 
 namespace WebAssembly {
 
+enum WasmAddressSpace : unsigned {
+  // Default address space, for pointers to linear memory (stack, heap, data).
+  WASM_ADDRESS_SPACE_DEFAULT = 0,
+  // A non-integral address space for pointers to named objects outside of
+  // linear memory: WebAssembly globals or WebAssembly locals.  Loads and stores
+  // to these pointers are lowered to global.get / global.set or local.get /
+  // local.set, as appropriate.
+  WASM_ADDRESS_SPACE_WASM_VAR = 1
+};
+
+inline bool isDefaultAddressSpace(unsigned AS) {
+  return AS == WASM_ADDRESS_SPACE_DEFAULT;
+}
+inline bool isWasmVarAddressSpace(unsigned AS) {
+  return AS == WASM_ADDRESS_SPACE_WASM_VAR;
+}
+inline bool isValidAddressSpace(unsigned AS) {
+  return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS);
+}
+
 bool isChild(const MachineInstr &MI, const WebAssemblyFunctionInfo &MFI);
 bool mayThrow(const MachineInstr &MI);
 

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
index 68e4b9c31b7c..cb0cdf1d8f98 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp
@@ -1182,6 +1182,8 @@ bool WebAssemblyFastISel::selectLoad(const Instruction *I) {
   const auto *Load = cast<LoadInst>(I);
   if (Load->isAtomic())
     return false;
+  if (!WebAssembly::isDefaultAddressSpace(Load->getPointerAddressSpace()))
+    return false;
   if (!Subtarget->hasSIMD128() && Load->getType()->isVectorTy())
     return false;
 
@@ -1240,6 +1242,8 @@ bool WebAssemblyFastISel::selectStore(const Instruction *I) {
   const auto *Store = cast<StoreInst>(I);
   if (Store->isAtomic())
     return false;
+  if (!WebAssembly::isDefaultAddressSpace(Store->getPointerAddressSpace()))
+    return false;
   if (!Subtarget->hasSIMD128() &&
       Store->getValueOperand()->getType()->isVectorTy())
     return false;

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
index 33fd2ae11154..e39e3055d09c 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -19,7 +19,7 @@ HANDLE_NODETYPE(RETURN)
 HANDLE_NODETYPE(ARGUMENT)
 // A wrapper node for TargetExternalSymbol, TargetGlobalAddress, and MCSymbol
 HANDLE_NODETYPE(Wrapper)
-// A special wapper used in PIC code for __memory_base/__table_base relcative
+// A special wapper used in PIC code for __memory_base/__table_base relative
 // access.
 HANDLE_NODETYPE(WrapperPIC)
 HANDLE_NODETYPE(BR_IF)
@@ -44,3 +44,5 @@ HANDLE_NODETYPE(MEMORY_FILL)
 
 // Memory intrinsics
 HANDLE_MEM_NODETYPE(LOAD_SPLAT)
+HANDLE_MEM_NODETYPE(GLOBAL_GET)
+HANDLE_MEM_NODETYPE(GLOBAL_SET)

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
index 9bcee5925340..7cd7ab69780f 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -17,13 +17,13 @@
 #include "WebAssemblyMachineFunctionInfo.h"
 #include "WebAssemblySubtarget.h"
 #include "WebAssemblyTargetMachine.h"
-#include "llvm/CodeGen/Analysis.h"
 #include "llvm/CodeGen/CallingConvLower.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
 #include "llvm/CodeGen/MachineJumpTableInfo.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/SelectionDAG.h"
+#include "llvm/CodeGen/SelectionDAGNodes.h"
 #include "llvm/CodeGen/WasmEHFuncInfo.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
@@ -69,6 +69,20 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering(
   // Compute derived properties from the register classes.
   computeRegisterProperties(Subtarget->getRegisterInfo());
 
+  // Transform loads and stores to pointers in address space 1 to loads and
+  // stores to WebAssembly global variables, outside linear memory.
+  for (auto T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) {
+    setOperationAction(ISD::LOAD, T, Custom);
+    setOperationAction(ISD::STORE, T, Custom);
+  }
+  if (Subtarget->hasSIMD128()) {
+    for (auto T : {MVT::v16i8, MVT::v8i16, MVT::v4i32, MVT::v4f32, MVT::v2i64,
+                   MVT::v2f64}) {
+      setOperationAction(ISD::LOAD, T, Custom);
+      setOperationAction(ISD::STORE, T, Custom);
+    }
+  }
+
   setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
   setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom);
   setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
@@ -1248,9 +1262,63 @@ SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op,
   case ISD::FP_TO_SINT_SAT:
   case ISD::FP_TO_UINT_SAT:
     return LowerFP_TO_INT_SAT(Op, DAG);
+  case ISD::LOAD:
+    return LowerLoad(Op, DAG);
+  case ISD::STORE:
+    return LowerStore(Op, DAG);
   }
 }
 
+static bool IsWebAssemblyGlobal(SDValue Op) {
+  if (const GlobalAddressSDNode *GA = dyn_cast<GlobalAddressSDNode>(Op))
+    return WebAssembly::isWasmVarAddressSpace(GA->getAddressSpace());
+
+  return false;
+}
+
+SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op,
+                                              SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  StoreSDNode *SN = cast<StoreSDNode>(Op.getNode());
+  const SDValue &Value = SN->getValue();
+  const SDValue &Base = SN->getBasePtr();
+  const SDValue &Offset = SN->getOffset();
+
+  if (IsWebAssemblyGlobal(Base)) {
+    if (!Offset->isUndef())
+      report_fatal_error("unexpected offset when storing to webassembly global",
+                         false);
+
+    SDVTList Tys = DAG.getVTList(MVT::Other);
+    SDValue Ops[] = {SN->getChain(), Value, Base};
+    return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops,
+                                   SN->getMemoryVT(), SN->getMemOperand());
+  }
+
+  return Op;
+}
+
+SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op,
+                                             SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  LoadSDNode *LN = cast<LoadSDNode>(Op.getNode());
+  const SDValue &Base = LN->getBasePtr();
+  const SDValue &Offset = LN->getOffset();
+
+  if (IsWebAssemblyGlobal(Base)) {
+    if (!Offset->isUndef())
+      report_fatal_error(
+          "unexpected offset when loading from webassembly global", false);
+
+    SDVTList Tys = DAG.getVTList(LN->getValueType(0), MVT::Other);
+    SDValue Ops[] = {LN->getChain(), Base};
+    return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_GET, DL, Tys, Ops,
+                                   LN->getMemoryVT(), LN->getMemOperand());
+  }
+
+  return Op;
+}
+
 SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
                                                   SelectionDAG &DAG) const {
   SDValue Src = Op.getOperand(2);
@@ -1369,8 +1437,8 @@ SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op,
   EVT VT = Op.getValueType();
   assert(GA->getTargetFlags() == 0 &&
          "Unexpected target flags on generic GlobalAddressSDNode");
-  if (GA->getAddressSpace() != 0)
-    fail(DL, DAG, "WebAssembly only expects the 0 address space");
+  if (!WebAssembly::isValidAddressSpace(GA->getAddressSpace()))
+    fail(DL, DAG, "Invalid address space for WebAssembly target");
 
   unsigned OperandFlags = 0;
   if (isPositionIndependent()) {

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
index 2f7195b17c42..632c039deac0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -120,6 +120,8 @@ class WebAssemblyTargetLowering final : public TargetLowering {
   SDValue LowerAccessVectorElement(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerShift(SDValue Op, SelectionDAG &DAG) const;
   SDValue LowerFP_TO_INT_SAT(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const;
+  SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const;
 
   // Custom DAG combine hooks
   SDValue

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
index 241112d9416a..72efe7b6fd72 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -79,6 +79,8 @@ def SDT_WebAssemblyWrapperPIC : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
                                                      SDTCisPtrTy<0>]>;
 def SDT_WebAssemblyThrow      : SDTypeProfile<0, -1, []>;
 def SDT_WebAssemblyCatch      : SDTypeProfile<1, 1, [SDTCisPtrTy<0>]>;
+def SDT_WebAssemblyGlobalGet  : SDTypeProfile<1, 1, [SDTCisPtrTy<1>]>;
+def SDT_WebAssemblyGlobalSet  : SDTypeProfile<0, 2, [SDTCisPtrTy<1>]>;
 
 //===----------------------------------------------------------------------===//
 // WebAssembly-specific DAG Nodes.
@@ -106,6 +108,12 @@ def WebAssemblythrow : SDNode<"WebAssemblyISD::THROW", SDT_WebAssemblyThrow,
                               [SDNPHasChain, SDNPVariadic]>;
 def WebAssemblycatch : SDNode<"WebAssemblyISD::CATCH", SDT_WebAssemblyCatch,
                               [SDNPHasChain, SDNPSideEffect]>;
+def WebAssemblyglobal_get :
+    SDNode<"WebAssemblyISD::GLOBAL_GET", SDT_WebAssemblyGlobalGet,
+           [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>;
+def WebAssemblyglobal_set :
+    SDNode<"WebAssemblyISD::GLOBAL_SET", SDT_WebAssemblyGlobalSet,
+           [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
 
 //===----------------------------------------------------------------------===//
 // WebAssembly-specific Operands.
@@ -241,12 +249,12 @@ include "WebAssemblyInstrFormats.td"
 // Additional instructions.
 //===----------------------------------------------------------------------===//
 
-multiclass ARGUMENT<WebAssemblyRegClass reg, ValueType vt> {
+multiclass ARGUMENT<WebAssemblyRegClass rc, ValueType vt> {
   let hasSideEffects = 1, isCodeGenOnly = 1, Defs = []<Register>,
       Uses = [ARGUMENTS] in
   defm ARGUMENT_#vt :
-    I<(outs reg:$res), (ins i32imm:$argno), (outs), (ins i32imm:$argno),
-      [(set (vt reg:$res), (WebAssemblyargument timm:$argno))]>;
+    I<(outs rc:$res), (ins i32imm:$argno), (outs), (ins i32imm:$argno),
+      [(set (vt rc:$res), (WebAssemblyargument timm:$argno))]>;
 }
 defm "": ARGUMENT<I32, i32>;
 defm "": ARGUMENT<I64, i64>;
@@ -257,7 +265,7 @@ defm "": ARGUMENT<EXTERNREF, externref>;
 
 // local.get and local.set are not generated by instruction selection; they
 // are implied by virtual register uses and defs.
-multiclass LOCAL<WebAssemblyRegClass vt, Operand global_op> {
+multiclass LOCAL<WebAssemblyRegClass rc, Operand global_op> {
   let hasSideEffects = 0 in {
   // COPY is not an actual instruction in wasm, but since we allow local.get and
   // local.set to be implicit during most of codegen, we can have a COPY which
@@ -265,21 +273,21 @@ multiclass LOCAL<WebAssemblyRegClass vt, Operand global_op> {
   // and local.set. COPYs are eliminated (and replaced with
   // local.get/local.set) in the ExplicitLocals pass.
   let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
-  defm COPY_#vt : I<(outs vt:$res), (ins vt:$src), (outs), (ins), [],
+  defm COPY_#rc : I<(outs rc:$res), (ins rc:$src), (outs), (ins), [],
                     "local.copy\t$res, $src", "local.copy">;
 
   // TEE is similar to COPY, but writes two copies of its result. Typically
   // this would be used to stackify one result and write the other result to a
   // local.
   let isAsCheapAsAMove = 1, isCodeGenOnly = 1 in
-  defm TEE_#vt : I<(outs vt:$res, vt:$also), (ins vt:$src), (outs), (ins), [],
+  defm TEE_#rc : I<(outs rc:$res, rc:$also), (ins rc:$src), (outs), (ins), [],
                    "local.tee\t$res, $also, $src", "local.tee">;
 
   // This is the actual local.get instruction in wasm. These are made explicit
   // by the ExplicitLocals pass. It has mayLoad because it reads from a wasm
   // local, which is a side effect not otherwise modeled in LLVM.
   let mayLoad = 1, isAsCheapAsAMove = 1 in
-  defm LOCAL_GET_#vt : I<(outs vt:$res), (ins local_op:$local),
+  defm LOCAL_GET_#rc : I<(outs rc:$res), (ins local_op:$local),
                          (outs), (ins local_op:$local), [],
                          "local.get\t$res, $local", "local.get\t$local", 0x20>;
 
@@ -287,7 +295,7 @@ multiclass LOCAL<WebAssemblyRegClass vt, Operand global_op> {
   // by the ExplicitLocals pass. It has mayStore because it writes to a wasm
   // local, which is a side effect not otherwise modeled in LLVM.
   let mayStore = 1, isAsCheapAsAMove = 1 in
-  defm LOCAL_SET_#vt : I<(outs), (ins local_op:$local, vt:$src),
+  defm LOCAL_SET_#rc : I<(outs), (ins local_op:$local, rc:$src),
                          (outs), (ins local_op:$local), [],
                          "local.set\t$local, $src", "local.set\t$local", 0x21>;
 
@@ -295,28 +303,36 @@ multiclass LOCAL<WebAssemblyRegClass vt, Operand global_op> {
   // LOCAL_TEEs by the ExplicitLocals pass. It has mayStore for the same reason
   // as LOCAL_SET.
   let mayStore = 1, isAsCheapAsAMove = 1 in
-  defm LOCAL_TEE_#vt : I<(outs vt:$res), (ins local_op:$local, vt:$src),
+  defm LOCAL_TEE_#rc : I<(outs rc:$res), (ins local_op:$local, rc:$src),
                          (outs), (ins local_op:$local), [],
                          "local.tee\t$res, $local, $src", "local.tee\t$local",
                          0x22>;
 
   // Unused values must be dropped in some contexts.
-  defm DROP_#vt : I<(outs), (ins vt:$src), (outs), (ins), [],
+  defm DROP_#rc : I<(outs), (ins rc:$src), (outs), (ins), [],
                     "drop\t$src", "drop", 0x1a>;
 
   let mayLoad = 1 in
-  defm GLOBAL_GET_#vt : I<(outs vt:$res), (ins global_op:$local),
-                          (outs), (ins global_op:$local), [],
-                          "global.get\t$res, $local", "global.get\t$local",
+  defm GLOBAL_GET_#rc : I<(outs rc:$res), (ins global_op:$addr),
+                          (outs), (ins global_op:$addr), [],
+                           "global.get\t$res, $addr", "global.get\t$addr",
                           0x23>;
 
   let mayStore = 1 in
-  defm GLOBAL_SET_#vt : I<(outs), (ins global_op:$local, vt:$src),
-                          (outs), (ins global_op:$local), [],
-                          "global.set\t$local, $src", "global.set\t$local",
+  defm GLOBAL_SET_#rc : I<(outs), (ins global_op:$addr, rc:$src),
+                          (outs), (ins global_op:$addr), [],
+                          "global.set\t$addr, $src", "global.set\t$addr",
                           0x24>;
 
-} // hasSideEffects = 0
+  } // hasSideEffects = 0
+  foreach vt = rc.RegTypes in {
+    def : Pat<(vt (WebAssemblyglobal_get
+                   (WebAssemblywrapper tglobaladdr:$addr))),
+              (!cast<NI>("GLOBAL_GET_" # rc) tglobaladdr:$addr)>;
+    def : Pat<(WebAssemblyglobal_set
+               vt:$src, (WebAssemblywrapper tglobaladdr:$addr)),
+              (!cast<NI>("GLOBAL_SET_" # rc) tglobaladdr:$addr, vt:$src)>;
+  }
 }
 defm "" : LOCAL<I32, global_op32>;
 defm "" : LOCAL<I64, global_op64>;  // 64-bit only needed for pointers.

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
index 7f324fc11210..ef9bd35d004a 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrRef.td
@@ -11,29 +11,29 @@
 ///
 //===----------------------------------------------------------------------===//
 
-multiclass REF_I<WebAssemblyRegClass reg, ValueType vt> {
-  defm REF_NULL_#reg : I<(outs reg:$res), (ins HeapType:$heaptype),
-                         (outs), (ins HeapType:$heaptype),
-                         [],
-                         "ref.null\t$res, $heaptype",
-                         "ref.null\t$heaptype",
-                         0xd0>,
-                       Requires<[HasReferenceTypes]>;
-  defm SELECT_#reg: I<(outs reg:$dst), (ins reg:$lhs, reg:$rhs, I32:$cond),
-                      (outs), (ins),
-                      [(set reg:$dst,
-                        (select I32:$cond, reg:$lhs, reg:$rhs))],
-                      vt#".select\t$dst, $lhs, $rhs, $cond",
-                      vt#".select", 0x1b>,
-                    Requires<[HasReferenceTypes]>;
+multiclass REF_I<WebAssemblyRegClass rc, ValueType vt> {
+  defm REF_NULL_#rc : I<(outs rc:$res), (ins HeapType:$heaptype),
+                        (outs), (ins HeapType:$heaptype),
+                        [],
+                        "ref.null\t$res, $heaptype",
+                        "ref.null\t$heaptype",
+                        0xd0>,
+                      Requires<[HasReferenceTypes]>;
+  defm SELECT_#rc: I<(outs rc:$dst), (ins rc:$lhs, rc:$rhs, I32:$cond),
+                     (outs), (ins),
+                     [(set rc:$dst,
+                       (select I32:$cond, rc:$lhs, rc:$rhs))],
+                     vt#".select\t$dst, $lhs, $rhs, $cond",
+                     vt#".select", 0x1b>,
+                   Requires<[HasReferenceTypes]>;
 }
 
 defm "" : REF_I<FUNCREF, funcref>;
 defm "" : REF_I<EXTERNREF, externref>;
 
-foreach reg = [FUNCREF, EXTERNREF] in {
-def : Pat<(select (i32 (setne I32:$cond, 0)), reg:$lhs, reg:$rhs),
-          (!cast<Instruction>("SELECT_"#reg) reg:$lhs, reg:$rhs, I32:$cond)>;
-def : Pat<(select (i32 (seteq I32:$cond, 0)), reg:$lhs, reg:$rhs),
-          (!cast<Instruction>("SELECT_"#reg) reg:$rhs, reg:$lhs, I32:$cond)>;
+foreach rc = [FUNCREF, EXTERNREF] in {
+def : Pat<(select (i32 (setne I32:$cond, 0)), rc:$lhs, rc:$rhs),
+          (!cast<Instruction>("SELECT_"#rc) rc:$lhs, rc:$rhs, I32:$cond)>;
+def : Pat<(select (i32 (seteq I32:$cond, 0)), rc:$lhs, rc:$rhs),
+          (!cast<Instruction>("SELECT_"#rc) rc:$rhs, rc:$lhs, I32:$cond)>;
 }

diff  --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 135055a43afc..94f4ae705e96 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -119,8 +119,9 @@ WebAssemblyTargetMachine::WebAssemblyTargetMachine(
     const TargetOptions &Options, Optional<Reloc::Model> RM,
     Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT)
     : LLVMTargetMachine(T,
-                        TT.isArch64Bit() ? "e-m:e-p:64:64-i64:64-n32:64-S128"
-                                         : "e-m:e-p:32:32-i64:64-n32:64-S128",
+                        TT.isArch64Bit()
+                            ? "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1"
+                            : "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1",
                         TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
                         getEffectiveCodeModel(CM, CodeModel::Large), OL),
       TLOF(new WebAssemblyTargetObjectFile()) {

diff  --git a/llvm/test/CodeGen/WebAssembly/global-get.ll b/llvm/test/CodeGen/WebAssembly/global-get.ll
new file mode 100644
index 000000000000..d683fe677864
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-get.ll
@@ -0,0 +1,54 @@
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false | FileCheck %s
+
+ at i32_global = local_unnamed_addr addrspace(1) global i32 undef
+ at i64_global = local_unnamed_addr addrspace(1) global i64 undef
+ at f32_global = local_unnamed_addr addrspace(1) global float undef
+ at f64_global = local_unnamed_addr addrspace(1) global double undef
+
+define i32 @return_i32_global() {
+; CHECK-LABEL: return_i32_global:
+; CHECK-NEXT: functype       return_i32_global () -> (i32)
+; CHECK-NEXT: global.get i32_global
+; CHECK-NEXT: end_function
+  %v = load i32, i32 addrspace(1)* @i32_global
+  ret i32 %v
+}
+
+define i64 @return_i64_global() {
+; CHECK-LABEL: return_i64_global:
+; CHECK-NEXT: functype       return_i64_global () -> (i64)
+; CHECK-NEXT: global.get i64_global
+; CHECK-NEXT: end_function
+  %v = load i64, i64 addrspace(1)* @i64_global
+  ret i64 %v
+}
+
+define float @return_f32_global() {
+; CHECK-LABEL: return_f32_global:
+; CHECK-NEXT: functype       return_f32_global () -> (f32)
+; CHECK-NEXT: global.get f32_global
+; CHECK-NEXT: end_function
+  %v = load float, float addrspace(1)* @f32_global
+  ret float %v
+}
+
+define double @return_f64_global() {
+; CHECK-LABEL: return_f64_global:
+; CHECK-NEXT: functype       return_f64_global () -> (f64)
+; CHECK-NEXT: global.get f64_global
+; CHECK-NEXT: end_function
+  %v = load double, double addrspace(1)* @f64_global
+  ret double %v
+}
+
+
+;; LLVM doesn't yet declare proper WebAssembly globals for these values,
+;; instead placing them in linear memory.  To fix in a followup.
+; FIXME-CHECK: .globl i32_global
+; FIXME-CHECK: .globaltype i32_global, i32
+; FIXME-CHECK: .globl i64_global
+; FIXME-CHECK: .globaltype i64_global, i64
+; FIXME-CHECK: .globl f32_global
+; FIXME-CHECK: .globaltype f32_global, f32
+; FIXME-CHECK: .globl f64_global
+; FIXME-CHECK: .globaltype f64_global, f64

diff  --git a/llvm/test/CodeGen/WebAssembly/global-set.ll b/llvm/test/CodeGen/WebAssembly/global-set.ll
new file mode 100644
index 000000000000..741fda632afd
--- /dev/null
+++ b/llvm/test/CodeGen/WebAssembly/global-set.ll
@@ -0,0 +1,57 @@
+; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false < %s | FileCheck %s
+
+ at i32_global = local_unnamed_addr addrspace(1) global i32 undef
+ at i64_global = local_unnamed_addr addrspace(1) global i64 undef
+ at f32_global = local_unnamed_addr addrspace(1) global float undef
+ at f64_global = local_unnamed_addr addrspace(1) global double undef
+
+define void @set_i32_global(i32 %v) {
+; CHECK-LABEL: set_i32_global:
+; CHECK-NEXT: functype       set_i32_global (i32) -> ()
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: global.set i32_global
+; CHECK-NEXT: end_function
+  store i32 %v, i32 addrspace(1)* @i32_global
+  ret void
+}
+
+define void @set_i64_global(i64 %v) {
+; CHECK-LABEL: set_i64_global:
+; CHECK-NEXT: functype       set_i64_global (i64) -> ()
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: global.set i64_global
+; CHECK-NEXT: end_function
+  store i64 %v, i64 addrspace(1)* @i64_global
+  ret void
+}
+
+define void @set_f32_global(float %v) {
+; CHECK-LABEL: set_f32_global:
+; CHECK-NEXT: functype       set_f32_global (f32) -> ()
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: global.set f32_global
+; CHECK-NEXT: end_function
+  store float %v, float addrspace(1)* @f32_global
+  ret void
+}
+
+define void @set_f64_global(double %v) {
+; CHECK-LABEL: set_f64_global:
+; CHECK-NEXT: functype       set_f64_global (f64) -> ()
+; CHECK-NEXT: local.get 0
+; CHECK-NEXT: global.set f64_global
+; CHECK-NEXT: end_function
+  store double %v, double addrspace(1)* @f64_global
+  ret void
+}
+
+;; LLVM doesn't yet declare proper WebAssembly globals for these values,
+;; instead placing them in linear memory.  To fix in a followup.
+; FIXME-CHECK: .globl i32_global
+; FIXME-CHECK: .globaltype i32_global, i32
+; FIXME-CHECK: .globl i64_global
+; FIXME-CHECK: .globaltype i64_global, i64
+; FIXME-CHECK: .globl f32_global
+; FIXME-CHECK: .globaltype f32_global, f32
+; FIXME-CHECK: .globl f64_global
+; FIXME-CHECK: .globaltype f64_global, f64


        


More information about the llvm-commits mailing list