mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
[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
This commit is contained in:
parent
156fb78f22
commit
839dc2d28e
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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()) {
|
||||
|
@ -120,6 +120,8 @@ private:
|
||||
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
|
||||
|
@ -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.
|
||||
|
@ -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)>;
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
54
test/CodeGen/WebAssembly/global-get.ll
Normal file
54
test/CodeGen/WebAssembly/global-get.ll
Normal file
@ -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
|
57
test/CodeGen/WebAssembly/global-set.ll
Normal file
57
test/CodeGen/WebAssembly/global-set.ll
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user