diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFastISel.cpp @@ -130,9 +130,13 @@ case MVT::i64: case MVT::f32: case MVT::f64: + return VT; case MVT::funcref: case MVT::externref: - return VT; + if (Subtarget->hasReferenceTypes()) + return VT; + else + break; case MVT::f16: return MVT::f32; case MVT::v16i8: diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def --- a/llvm/lib/Target/WebAssembly/WebAssemblyISD.def +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISD.def @@ -40,3 +40,7 @@ // Memory intrinsics HANDLE_MEM_NODETYPE(LOAD_SPLAT) + +// Reference Types +HANDLE_MEM_NODETYPE(GLOBAL_GET) +HANDLE_MEM_NODETYPE(GLOBAL_SET) diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -60,6 +60,7 @@ bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) override; + bool SelectExternRefAddr(const SDValue &Addr, const SDValue &Base); // Include the pieces autogenerated from the target description. #include "WebAssemblyGenDAGISel.inc" @@ -210,6 +211,12 @@ return true; } +// bool WebAssemblyDAGToDAGISel::SelectExternRefAddr( +// const SDValue &Addr, const SDValue &Base) { + +// return false; +// } + /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready /// for instruction scheduling. FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -66,6 +66,7 @@ bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, unsigned AS, Instruction *I = nullptr) const override; + bool isExternRefGlobal(SDValue Op) const; bool allowsMisalignedMemoryAccesses(EVT, unsigned AddrSpace, unsigned Align, MachineMemOperand::Flags Flags, bool *Fast) const override; @@ -119,6 +120,8 @@ SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerAccessVectorElement(SDValue Op, SelectionDAG &DAG) const; SDValue LowerShift(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/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/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" @@ -65,10 +66,16 @@ addRegisterClass(MVT::v4f32, &WebAssembly::V128RegClass); addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); + } else if (Subtarget->hasReferenceTypes()) { + addRegisterClass(MVT::externref, &WebAssembly::EXTERNREFRegClass); + addRegisterClass(MVT::funcref, &WebAssembly::FUNCREFRegClass); } // Compute derived properties from the register classes. computeRegisterProperties(Subtarget->getRegisterInfo()); + setOperationAction(ISD::LOAD, MVTPtr, Custom); + setOperationAction(ISD::STORE, MVTPtr, Custom); + setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); @@ -1240,9 +1247,110 @@ case ISD::SRA: case ISD::SRL: return LowerShift(Op, DAG); + case ISD::LOAD: + return LowerLoad(Op, DAG); + case ISD::STORE: + return LowerStore(Op, DAG); } } +bool WebAssemblyTargetLowering::isExternRefGlobal(SDValue Op) const { + const GlobalAddressSDNode *GA = dyn_cast(Op); + if (!GA || GA->getAddressSpace() != 1) + return false; + + return true; +} + +SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT PtrVT = getPointerTy(DAG.getDataLayout()); + + llvm::errs() << "Lowering store\n"; + StoreSDNode *SN = cast(Op.getNode()); + + const SDValue &Value = SN->getValue(); + + // Expect Offset to be undef for externref stores + const SDValue &Offset = SN->getOffset(); + if (!Offset->isUndef()) + return Op; + + // Expect Base to be an externref GlobalAddress + const SDValue &Base = SN->getBasePtr(); + if (!isExternRefGlobal(Base)) + return Op; + + // SDValue GlobalSet = DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, + // DAG.getVTList(MVT::externref), {LN->getChain(), Base}, LN->getMemoryVT(), + // LN->getMemOperand()); SDValue Result = DAG.getMergeValues({GlobalGet, + // LN->getChain()}, DL); + SDVTList Tys = DAG.getVTList(MVT::externref); + SDValue Ops[] = {SN->getChain(), Value, Base}; + return DAG.getMemIntrinsicNode(WebAssemblyISD::GLOBAL_SET, DL, Tys, Ops, + SN->getMemoryVT(), SN->getMemOperand()); +} + +SDValue WebAssemblyTargetLowering::LowerLoad(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT PtrVT = getPointerTy(DAG.getDataLayout()); + + llvm::errs() << "Lowering load\n"; + LoadSDNode *LN = cast(Op.getNode()); + + // If this this not in addrspace(1) bail out + if (LN->getAddressSpace() != 1) + return Op; + + const SDValue &Base = LN->getBasePtr(); + + // Check Base is a Global Address + if (!isExternRefGlobal(Base)) + return Op; + + // Check Offset if undef + const SDValue &Offset = LN->getOffset(); + if (!Offset->isUndef()) + return Op; + + // const Value *V = &Base; + // Type *ty = V->getType(); + // if (!ty->isPointerTy()) + // return Op; + + // PointerType *ptrTy = cast(ty); + // if(ptrTy->getAddressSpace() != 1 || + // !ptrTy->getElementType()->isPointerTy()) + // return Op; + + // ptrTy = cast(ptrTy->getElementType()); + // if (ptrTy->getAddressSpace() != 1 || + // !ptrTy->getElementType()->isStructTy()) + // return Op; + + // StructType *strTy = cast(ptrTy->getElementType()); + // if(!strTy->isOpaque() || + // !strTy->hasName() || + // strTy->getName() != "extern") + // return Op; + + // At this point, we now that the load is an externref and we can generate + // the proper node. + // SDValue GlobalAddr = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, + // PtrVT); SDValue NewNode = DAG.getNode(WebAssemblyISD::GLOBAL_GET, DL, + // PtrVT, GlobalAddr); + + SDValue GlobalGet = DAG.getMemIntrinsicNode( + WebAssemblyISD::GLOBAL_GET, DL, DAG.getVTList(MVT::externref), + {LN->getChain(), Base}, LN->getMemoryVT(), LN->getMemOperand()); + SDValue Result = DAG.getMergeValues({GlobalGet, LN->getChain()}, DL); + assert(Result->getNumValues() == 2 && "Loads must carry a chain!"); + + return Result; +} + SDValue WebAssemblyTargetLowering::LowerCopyToReg(SDValue Op, SelectionDAG &DAG) const { SDValue Src = Op.getOperand(2); @@ -1361,8 +1469,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()) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -1,3 +1,4 @@ + // WebAssemblyInstrInfo.td-Describe the WebAssembly Instructions-*- tablegen -*- // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -116,6 +116,22 @@ defm : LoadPatOffsetOnly; defm : LoadPatOffsetOnly; +// Global GET +def wasm_global_get_t : SDTypeProfile<1, 1, [SDTCisPtrTy<1>]>; +def wasm_global_get : SDNode<"WebAssemblyISD::GLOBAL_GET", wasm_global_get_t, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; +def : Pat<(externref (wasm_global_get (WebAssemblywrapper tglobaladdr:$addr))), + (GLOBAL_GET_EXTERNREF tglobaladdr:$addr)>, + Requires<[HasReferenceTypes]>; + +// Global SET +def wasm_global_set_t : SDTypeProfile<0, 2, [SDTCisPtrTy<1>]>; +def wasm_global_set : SDNode<"WebAssemblyISD::GLOBAL_SET", wasm_global_set_t, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; +def : Pat<(wasm_global_set externref:$v, (WebAssemblywrapper tglobaladdr:$addr)), + (GLOBAL_SET_EXTERNREF global_op:$addr, EXTERNREF:$v)>, + Requires<[HasReferenceTypes]>; + multiclass LoadPatGlobalAddrOffOnly { def : Pat<(ty (kind (WebAssemblywrapper tglobaladdr:$off))), (!cast(inst # "_A32") 0, tglobaladdr:$off, (CONST_I32 0))>, diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -119,8 +119,9 @@ const TargetOptions &Options, Optional RM, Optional 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()) {