wingo updated this revision to Diff 342719.
wingo added a comment.
Rebase on main
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D101608/new/
https://reviews.llvm.org/D101608
Files:
clang/lib/Basic/Targets/WebAssembly.h
clang/test/CodeGen/target-data.c
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/WebAssemblyTargetMachine.cpp
llvm/test/CodeGen/WebAssembly/global-get.ll
llvm/test/CodeGen/WebAssembly/global-set.ll
Index: llvm/test/CodeGen/WebAssembly/global-set.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/WebAssembly/global-set.ll
@@ -0,0 +1,57 @@
+; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false < %s | FileCheck %s
+
+@i32_global = local_unnamed_addr addrspace(1) global i32 undef
+@i64_global = local_unnamed_addr addrspace(1) global i64 undef
+@f32_global = local_unnamed_addr addrspace(1) global float undef
+@f64_global = local_unnamed_addr addrspace(1) global double undef
+
+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
Index: llvm/test/CodeGen/WebAssembly/global-get.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/WebAssembly/global-get.ll
@@ -0,0 +1,54 @@
+; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false | FileCheck %s
+
+@i32_global = local_unnamed_addr addrspace(1) global i32 undef
+@i64_global = local_unnamed_addr addrspace(1) global i64 undef
+@f32_global = local_unnamed_addr addrspace(1) global float undef
+@f64_global = local_unnamed_addr addrspace(1) global double undef
+
+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
Index: llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -119,8 +119,9 @@
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()) {
Index: llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
+++ llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td
@@ -79,6 +79,8 @@
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 @@
[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.
@@ -257,7 +265,7 @@
// 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 reg, 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,58 +273,66 @@
// 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), [],
- "local.copy\t$res, $src", "local.copy">;
+ defm COPY_#reg : I<(outs reg:$res), (ins reg:$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), [],
- "local.tee\t$res, $also, $src", "local.tee">;
+ defm TEE_#reg : I<(outs reg:$res, reg:$also), (ins reg:$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),
- (outs), (ins local_op:$local), [],
- "local.get\t$res, $local", "local.get\t$local", 0x20>;
+ defm LOCAL_GET_#reg : I<(outs reg:$res), (ins local_op:$local),
+ (outs), (ins local_op:$local), [],
+ "local.get\t$res, $local", "local.get\t$local", 0x20>;
// This is the actual local.set instruction in wasm. These are made explicit
// 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),
- (outs), (ins local_op:$local), [],
- "local.set\t$local, $src", "local.set\t$local", 0x21>;
+ defm LOCAL_SET_#reg : I<(outs), (ins local_op:$local, reg:$src),
+ (outs), (ins local_op:$local), [],
+ "local.set\t$local, $src", "local.set\t$local", 0x21>;
// This is the actual local.tee instruction in wasm. TEEs are turned into
// 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),
- (outs), (ins local_op:$local), [],
- "local.tee\t$res, $local, $src", "local.tee\t$local",
- 0x22>;
+ defm LOCAL_TEE_#reg : I<(outs reg:$res), (ins local_op:$local, reg:$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), [],
- "drop\t$src", "drop", 0x1a>;
+ defm DROP_#reg : I<(outs), (ins reg:$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",
- 0x23>;
+ defm GLOBAL_GET_#reg : I<(outs reg:$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",
- 0x24>;
-
-} // hasSideEffects = 0
+ defm GLOBAL_SET_#reg : I<(outs), (ins global_op:$addr, reg:$src),
+ (outs), (ins global_op:$addr), [],
+ "global.set\t$addr, $src", "global.set\t$addr",
+ 0x24>;
+
+ } // hasSideEffects = 0
+ foreach vt = reg.RegTypes in {
+ def : Pat<(vt (WebAssemblyglobal_get
+ (WebAssemblywrapper tglobaladdr:$addr))),
+ (!cast<NI>("GLOBAL_GET_" # reg) tglobaladdr:$addr)>;
+ def : Pat<(WebAssemblyglobal_set
+ vt:$src, (WebAssemblywrapper tglobaladdr:$addr)),
+ (!cast<NI>("GLOBAL_SET_" # reg) tglobaladdr:$addr, vt:$src)>;
+ }
}
defm "" : LOCAL<I32, global_op32>;
defm "" : LOCAL<I64, global_op64>; // 64-bit only needed for pointers.
Index: llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
+++ llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h
@@ -45,6 +45,14 @@
WebAssemblyTargetLowering(const TargetMachine &TM,
const WebAssemblySubtarget &STI);
+ enum WasmAddressSpace {
+ // WebAssembly uses the following address spaces:
+ // AS 0 : is the default address space for values in linear memory
+ DEFAULT = 0,
+ // AS 1 : is a non-integral address space for global variables
+ GLOBAL = 1,
+ };
+
private:
/// Keep a pointer to the WebAssemblySubtarget around so that we can make the
/// right decision when generating code for different targets.
@@ -120,6 +128,8 @@
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
Index: llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
+++ llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
@@ -24,6 +24,7 @@
#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 +70,20 @@
// 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);
@@ -1253,9 +1268,65 @@
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 GA->getAddressSpace() ==
+ WebAssemblyTargetLowering::WasmAddressSpace::GLOBAL;
+
+ 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 &Offset = SN->getOffset();
+ const SDValue &Base = SN->getBasePtr();
+
+ 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);
+
+ EVT VT = LN->getValueType(0);
+ SDValue GlobalGet = DAG.getMemIntrinsicNode(
+ WebAssemblyISD::GLOBAL_GET, DL, DAG.getVTList(VT),
+ {LN->getChain(), Base}, LN->getMemoryVT(), LN->getMemOperand());
+ return DAG.getMergeValues({GlobalGet, LN->getChain()}, DL);
+ }
+
+ return Op;
+}
+
SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op,
SelectionDAG &DAG) const {
SDValue Src = Op.getOperand(2);
@@ -1374,8 +1445,6 @@
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");
unsigned OperandFlags = 0;
if (isPositionIndependent()) {
Index: llvm/lib/Target/WebAssembly/WebAssemblyISD.def
===================================================================
--- llvm/lib/Target/WebAssembly/WebAssemblyISD.def
+++ llvm/lib/Target/WebAssembly/WebAssemblyISD.def
@@ -19,7 +19,7 @@
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,7 @@
// Memory intrinsics
HANDLE_MEM_NODETYPE(LOAD_SPLAT)
+
+// Reference Types
+HANDLE_MEM_NODETYPE(GLOBAL_GET)
+HANDLE_MEM_NODETYPE(GLOBAL_SET)
Index: clang/test/CodeGen/target-data.c
===================================================================
--- clang/test/CodeGen/target-data.c
+++ 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
Index: clang/lib/Basic/Targets/WebAssembly.h
===================================================================
--- clang/lib/Basic/Targets/WebAssembly.h
+++ clang/lib/Basic/Targets/WebAssembly.h
@@ -147,7 +147,7 @@
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 @@
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:
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits