diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h --- a/llvm/include/llvm-c/Core.h +++ b/llvm/include/llvm-c/Core.h @@ -163,7 +163,9 @@ LLVMTokenTypeKind, /**< Tokens */ LLVMScalableVectorTypeKind, /**< Scalable SIMD vector type */ LLVMBFloatTypeKind, /**< 16 bit brain floating point type */ - LLVMX86_AMXTypeKind /**< X86 AMX */ + LLVMX86_AMXTypeKind, /**< X86 AMX */ + LLVMWasm_FuncrefTypeKind, /**< Wasm Funcref type */ + LLVMWasm_ExternrefTypeKind /**< Wasm Externref type */ } LLVMTypeKind; typedef enum { @@ -1494,6 +1496,16 @@ */ LLVMTypeRef LLVMX86MMXTypeInContext(LLVMContextRef C); +/** + * Create a WebAssembly externref type in a context. + */ +LLVMTypeRef LLVMWasmExternrefTypeInContext(LLVMContextRef C); + +/** + * Create a WebAssembly funcref type in a context. + */ +LLVMTypeRef LLVMWasmFuncrefTypeInContext(LLVMContextRef C); + /** * Create a X86 AMX type in a context. */ diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -169,7 +169,9 @@ TYPE_CODE_TOKEN = 22, // TOKEN TYPE_CODE_BFLOAT = 23, // BRAIN FLOATING POINT - TYPE_CODE_X86_AMX = 24 // X86 AMX + TYPE_CODE_X86_AMX = 24, // X86 AMX + TYPE_CODE_WASM_FUNCREF = 25, // WEBASSEMBLY FUNCREF + TYPE_CODE_WASM_EXTERNREF = 26 // WEBASSEMBLY EXTERNREF }; enum OperandBundleTagCode { diff --git a/llvm/include/llvm/CodeGen/ValueTypes.h b/llvm/include/llvm/CodeGen/ValueTypes.h --- a/llvm/include/llvm/CodeGen/ValueTypes.h +++ b/llvm/include/llvm/CodeGen/ValueTypes.h @@ -120,6 +120,11 @@ return changeExtendedTypeToInteger(); } + /// Test if the given EVT has zero size + bool isZeroSized() const { + return getSizeInBits() == 0; + } + /// Test if the given EVT is simple (as opposed to being extended). bool isSimple() const { return V.SimpleTy != MVT::INVALID_SIMPLE_VALUE_TYPE; diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -199,7 +199,6 @@ def externref : ValueType<0, 164>; // WebAssembly's externref type def x86amx : ValueType<8192, 165>; // X86 AMX value - def token : ValueType<0 , 248>; // TokenTy def MetadataVT: ValueType<0,249>; // Metadata diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h --- a/llvm/include/llvm/IR/Type.h +++ b/llvm/include/llvm/IR/Type.h @@ -67,6 +67,8 @@ X86_MMXTyID, ///< MMX vectors (64 bits, X86 specific) X86_AMXTyID, ///< AMX vectors (8192 bits, X86 specific) TokenTyID, ///< Tokens + Wasm_ExternrefTyID, ///< Externref Type (WebAssembly specific) + Wasm_FuncrefTyID, ///< Funcref Type (WebAssembly specific) // Derived types... see DerivedTypes.h file. IntegerTyID, ///< Arbitrary bit width integers @@ -186,6 +188,15 @@ /// Return true if this is X86 AMX. bool isX86_AMXTy() const { return getTypeID() == X86_AMXTyID; } + /// Return true if this is externref. + bool isWasm_ExternrefTy() const { return getTypeID() == Wasm_ExternrefTyID; } + + /// Return true if this is funcref. + bool isWasm_FuncrefTy() const { return getTypeID() == Wasm_FuncrefTyID; } + + /// Return true if this is a reference type + bool isWasm_ReferenceTypeTy() const { return isWasm_ExternrefTy() || isWasm_FuncrefTy(); } + /// Return true if this is a FP type or a vector of FP. bool isFPOrFPVectorTy() const { return getScalarType()->isFloatingPointTy(); } @@ -414,6 +425,8 @@ static Type *getPPC_FP128Ty(LLVMContext &C); static Type *getX86_MMXTy(LLVMContext &C); static Type *getX86_AMXTy(LLVMContext &C); + static Type *getWasm_ExternrefTy(LLVMContext &C); + static Type *getWasm_FuncrefTy(LLVMContext &C); static Type *getTokenTy(LLVMContext &C); static IntegerType *getIntNTy(LLVMContext &C, unsigned N); static IntegerType *getInt1Ty(LLVMContext &C); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -919,6 +919,8 @@ case Type::X86_MMXTyID: Code = bitc::TYPE_CODE_X86_MMX; break; case Type::X86_AMXTyID: Code = bitc::TYPE_CODE_X86_AMX; break; case Type::TokenTyID: Code = bitc::TYPE_CODE_TOKEN; break; + case Type::Wasm_FuncrefTyID: Code = bitc::TYPE_CODE_WASM_FUNCREF; break; + case Type::Wasm_ExternrefTyID: Code = bitc::TYPE_CODE_WASM_EXTERNREF; break; case Type::IntegerTyID: // INTEGER: [width] Code = bitc::TYPE_CODE_INTEGER; diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -6450,6 +6450,10 @@ EVT LoadResultVT = TLI->getValueType(*DL, Load->getType()); unsigned BitWidth = LoadResultVT.getSizeInBits(); + // If the BitWidth is 0, do not try to optimize the type + if (BitWidth == 0) + return false; + APInt DemandBits(BitWidth, 0); APInt WidestAndBits(BitWidth, 0); diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -1167,8 +1167,12 @@ << "unknown-address"; } MachineOperand::printOperandOffset(OS, getOffset()); - if (getAlign() != getSize()) - OS << ", align " << getAlign().value(); + if (getSize() > 0) { + if (getAlign() != getSize()) + OS << ", align " << getAlign().value(); + } + else + OS << ", opaque "; if (getAlign() != getBaseAlign()) OS << ", basealign " << getBaseAlign().value(); auto AAInfo = getAAInfo(); diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -22968,6 +22968,10 @@ if (BasePtr.getBase().isUndef()) return false; + // Do not handle stores to opaque types + if (St->getMemoryVT().isZeroSized()) + return false; + // BaseIndexOffset assumes that offsets are fixed-size, which // is not valid for scalable vectors where the offsets are // scaled by `vscale`, so bail out early. diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4080,7 +4080,8 @@ SmallVector Values(NumValues); SmallVector Chains(std::min(MaxParallelChains, NumValues)); EVT PtrVT = Ptr.getValueType(); - + EVT EltVT = PtrVT.getScalarType(); + MachineMemOperand::Flags MMOFlags = TLI.getLoadMemOperandFlags(I, DAG.getDataLayout()); @@ -4099,17 +4100,23 @@ Root = Chain; ChainI = 0; } - SDValue A = DAG.getNode(ISD::ADD, dl, - PtrVT, Ptr, - DAG.getConstant(Offsets[i], dl, PtrVT), - Flags); + SDValue A; + + if (EltVT.getSizeInBits() == 0) + A = Ptr; + else + A = DAG.getNode(ISD::ADD, dl, + PtrVT, Ptr, + DAG.getConstant(Offsets[i], dl, PtrVT), + Flags); SDValue L = DAG.getLoad(MemVTs[i], dl, Root, A, MachinePointerInfo(SV, Offsets[i]), Alignment, MMOFlags, AAInfo, Ranges); Chains[ChainI] = L.getValue(1); - if (MemVTs[i] != ValueVTs[i]) + // Skip ZExt or Trunc if a ValueVT is zero sized + if (ValueVTs[i].getSizeInBits() > 0 && MemVTs[i] != ValueVTs[i]) L = DAG.getZExtOrTrunc(L, dl, ValueVTs[i]); Values[i] = L; @@ -4248,7 +4255,8 @@ SDValue Add = DAG.getMemBasePlusOffset(Ptr, TypeSize::Fixed(Offsets[i]), dl, Flags); SDValue Val = SDValue(Src.getNode(), Src.getResNo() + i); - if (MemVTs[i] != ValueVTs[i]) + // Skip ZExt or Trunc if a ValueVT is zero sized + if (ValueVTs[i].getSizeInBits() > 0 && MemVTs[i] != ValueVTs[i]) Val = DAG.getPtrExtOrTrunc(Val, dl, MemVTs[i]); SDValue St = DAG.getStore(Root, dl, Val, Add, MachinePointerInfo(PtrV, Offsets[i]), diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -1693,7 +1693,7 @@ // For example, the ABI alignment may change based on software platform while // this function should only be affected by hardware implementation. Type *Ty = VT.getTypeForEVT(Context); - if (Alignment >= DL.getABITypeAlign(Ty)) { + if (!VT.isZeroSized() && Alignment >= DL.getABITypeAlign(Ty)) { // Assume that an access that meets the ABI-specified alignment is fast. if (Fast != nullptr) *Fast = true; diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -199,6 +199,8 @@ case MVT::ppcf128: return Type::getPPC_FP128Ty(Context); case MVT::x86mmx: return Type::getX86_MMXTy(Context); case MVT::x86amx: return Type::getX86_AMXTy(Context); + case MVT::externref: return Type::getWasm_ExternrefTy(Context); + case MVT::funcref: return Type::getWasm_FuncrefTy(Context); case MVT::v1i1: return FixedVectorType::get(Type::getInt1Ty(Context), 1); case MVT::v2i1: diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -611,6 +611,8 @@ case Type::X86_MMXTyID: OS << "x86_mmx"; return; case Type::X86_AMXTyID: OS << "x86_amx"; return; case Type::TokenTyID: OS << "token"; return; + case Type::Wasm_FuncrefTyID: OS << "funcref"; return; + case Type::Wasm_ExternrefTyID: OS << "externref"; return; case Type::IntegerTyID: OS << 'i' << cast(Ty)->getBitWidth(); return; diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp --- a/llvm/lib/IR/Core.cpp +++ b/llvm/lib/IR/Core.cpp @@ -517,6 +517,10 @@ return LLVMTokenTypeKind; case Type::ScalableVectorTyID: return LLVMScalableVectorTypeKind; + case Type::Wasm_FuncrefTyID: + return LLVMWasm_FuncrefTypeKind; + case Type::Wasm_ExternrefTyID: + return LLVMWasm_ExternrefTypeKind; } llvm_unreachable("Unhandled TypeID."); } @@ -624,6 +628,12 @@ LLVMTypeRef LLVMX86MMXTypeInContext(LLVMContextRef C) { return (LLVMTypeRef) Type::getX86_MMXTy(*unwrap(C)); } +LLVMTypeRef LLVMWasmFuncrefTypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getWasm_FuncrefTy(*unwrap(C)); +} +LLVMTypeRef LLVMWasmExternrefTypeInContext(LLVMContextRef C) { + return (LLVMTypeRef) Type::getWasm_ExternrefTy(*unwrap(C)); +} LLVMTypeRef LLVMX86AMXTypeInContext(LLVMContextRef C) { return (LLVMTypeRef) Type::getX86_AMXTy(*unwrap(C)); } diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1429,6 +1429,7 @@ Type VoidTy, LabelTy, HalfTy, BFloatTy, FloatTy, DoubleTy, MetadataTy, TokenTy; Type X86_FP80Ty, FP128Ty, PPC_FP128Ty, X86_MMXTy, X86_AMXTy; + Type Wasm_ExternrefTy, Wasm_FuncrefTy; IntegerType Int1Ty, Int8Ty, Int16Ty, Int32Ty, Int64Ty, Int128Ty; BumpPtrAllocator Alloc; diff --git a/llvm/lib/IR/LLVMContextImpl.cpp b/llvm/lib/IR/LLVMContextImpl.cpp --- a/llvm/lib/IR/LLVMContextImpl.cpp +++ b/llvm/lib/IR/LLVMContextImpl.cpp @@ -22,26 +22,17 @@ using namespace llvm; LLVMContextImpl::LLVMContextImpl(LLVMContext &C) - : DiagHandler(std::make_unique()), - VoidTy(C, Type::VoidTyID), - LabelTy(C, Type::LabelTyID), - HalfTy(C, Type::HalfTyID), - BFloatTy(C, Type::BFloatTyID), - FloatTy(C, Type::FloatTyID), - DoubleTy(C, Type::DoubleTyID), - MetadataTy(C, Type::MetadataTyID), - TokenTy(C, Type::TokenTyID), - X86_FP80Ty(C, Type::X86_FP80TyID), - FP128Ty(C, Type::FP128TyID), - PPC_FP128Ty(C, Type::PPC_FP128TyID), - X86_MMXTy(C, Type::X86_MMXTyID), - X86_AMXTy(C, Type::X86_AMXTyID), - Int1Ty(C, 1), - Int8Ty(C, 8), - Int16Ty(C, 16), - Int32Ty(C, 32), - Int64Ty(C, 64), - Int128Ty(C, 128) {} + : DiagHandler(std::make_unique()), + VoidTy(C, Type::VoidTyID), LabelTy(C, Type::LabelTyID), + HalfTy(C, Type::HalfTyID), BFloatTy(C, Type::BFloatTyID), + FloatTy(C, Type::FloatTyID), DoubleTy(C, Type::DoubleTyID), + MetadataTy(C, Type::MetadataTyID), TokenTy(C, Type::TokenTyID), + X86_FP80Ty(C, Type::X86_FP80TyID), FP128Ty(C, Type::FP128TyID), + PPC_FP128Ty(C, Type::PPC_FP128TyID), X86_MMXTy(C, Type::X86_MMXTyID), + X86_AMXTy(C, Type::X86_AMXTyID), + Wasm_ExternrefTy(C, Type::Wasm_ExternrefTyID), + Wasm_FuncrefTy(C, Type::Wasm_FuncrefTyID), Int1Ty(C, 1), Int8Ty(C, 8), + Int16Ty(C, 16), Int32Ty(C, 32), Int64Ty(C, 64), Int128Ty(C, 128) {} LLVMContextImpl::~LLVMContextImpl() { // NOTE: We need to delete the contents of OwnedModules, but Module's dtor diff --git a/llvm/lib/IR/Type.cpp b/llvm/lib/IR/Type.cpp --- a/llvm/lib/IR/Type.cpp +++ b/llvm/lib/IR/Type.cpp @@ -197,6 +197,8 @@ Type *Type::getPPC_FP128Ty(LLVMContext &C) { return &C.pImpl->PPC_FP128Ty; } Type *Type::getX86_MMXTy(LLVMContext &C) { return &C.pImpl->X86_MMXTy; } Type *Type::getX86_AMXTy(LLVMContext &C) { return &C.pImpl->X86_AMXTy; } +Type *Type::getWasm_ExternrefTy(LLVMContext &C) { return &C.pImpl->Wasm_ExternrefTy; } +Type *Type::getWasm_FuncrefTy(LLVMContext &C) { return &C.pImpl->Wasm_FuncrefTy; } IntegerType *Type::getInt1Ty(LLVMContext &C) { return &C.pImpl->Int1Ty; } IntegerType *Type::getInt8Ty(LLVMContext &C) { return &C.pImpl->Int8Ty; } 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 @@ -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) @@ -40,3 +40,8 @@ // Memory intrinsics HANDLE_MEM_NODETYPE(LOAD_SPLAT) + +// Reference Types +HANDLE_MEM_NODETYPE(GLOBAL_GET) +HANDLE_MEM_NODETYPE(GLOBAL_SET) +HANDLE_MEM_NODETYPE(TABLE_SET_FUNCREF) 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" 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 @@ -45,6 +45,18 @@ WebAssemblyTargetLowering(const TargetMachine &TM, const WebAssemblySubtarget &STI); + MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override { + if (AS == 1) + return MVT::externref; + else if (AS == 3) + return MVT::funcref; + return TargetLowering::getPointerTy(DL, AS); + } + + /// Return the correct alignment for the current calling convention. + Align getABIAlignmentForCallingConv(Type *ArgTy, + DataLayout DL) const override; + private: /// Keep a pointer to the WebAssemblySubtarget around so that we can make the /// right decision when generating code for different targets. @@ -66,6 +78,9 @@ bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, unsigned AS, Instruction *I = nullptr) const override; + bool isExternrefGlobal(SDValue Op) const; + bool isFuncrefGlobal(SDValue Op) const; + bool isFuncref(const Value *Op) const; bool allowsMisalignedMemoryAccesses(EVT, unsigned AddrSpace, Align Alignment, MachineMemOperand::Flags Flags, bool *Fast) const override; @@ -100,6 +115,11 @@ report_fatal_error("llvm.clear_cache is not supported on wasm"); } + bool splitValueIntoRegisterParts(SelectionDAG &DAG, const SDLoc &DL, + SDValue Val, SDValue *Parts, + unsigned NumParts, MVT PartVT, + Optional CC) const override; + // Custom lowering hooks. SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; SDValue LowerFrameIndex(SDValue Op, SelectionDAG &DAG) const; @@ -119,6 +139,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,17 @@ 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, MVT::externref, Custom); + setOperationAction(ISD::STORE, MVT::funcref, Custom); + setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); setOperationAction(ISD::GlobalTLSAddress, MVTPtr, Custom); setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom); @@ -438,6 +446,16 @@ bool IsIndirect = CallParams.getOperand(0).isReg(); bool IsRetCall = CallResults.getOpcode() == WebAssembly::RET_CALL_RESULTS; + bool IsFuncrefCall = false; + if (IsIndirect) { + Register Reg = CallParams.getOperand(0).getReg(); + const MachineFunction *MF = BB->getParent(); + const MachineRegisterInfo &MRI = MF->getRegInfo(); + const TargetRegisterClass *TRC = MRI.getRegClass(Reg); + IsFuncrefCall = (TRC == &WebAssembly::FUNCREFRegClass); + assert(!IsFuncrefCall || Subtarget->hasReferenceTypes()); + } + unsigned CallOp; if (IsIndirect && IsRetCall) { CallOp = WebAssembly::RET_CALL_INDIRECT; @@ -481,8 +499,9 @@ // Placeholder for the type index. MIB.addImm(0); // The table into which this call_indirect indexes. - MCSymbolWasm *Table = - WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget); + MCSymbolWasm *Table = IsFuncrefCall ? + WebAssembly::getOrCreateFuncrefCallTableSymbol(MF.getContext(), Subtarget) : + WebAssembly::getOrCreateFunctionTableSymbol(MF.getContext(), Subtarget); if (Subtarget->hasReferenceTypes()) { MIB.addSym(Table); } else { @@ -822,7 +841,7 @@ "calling conventions yet"); if (CLI.IsPatchPoint) fail(DL, DAG, "WebAssembly doesn't support patch point yet"); - + if (CLI.IsTailCall) { auto NoTail = [&](const char *Msg) { if (CLI.CB && CLI.CB->isMustTailCall()) @@ -1039,6 +1058,34 @@ InTys.push_back(In.VT); } + // Lastly, if this is a call to a funcref we need to add an instruction + // table.set to the chain and transform the call. + if (CLI.CB && isFuncref(CLI.CB->getCalledOperand())) { + // In the absence of function references proposal where a funcref call is lowered to call_ref, + // using reference types we generate a table.set to set the funcref to a special table used + // solely for this purpose, followed by a call_indirect. + // Here we just generate the table set, and return the SDValue of the table.set so that + // LowerCall can finalize the lowering by generating the call_indirect. + SDValue Chain = Ops[0]; + + MCSymbolWasm *Table = + WebAssembly::getOrCreateFuncrefCallTableSymbol(MF.getContext(), Subtarget); + SDValue Sym = DAG.getMCSymbol(Table, PtrVT); + SDValue TableSlot = DAG.getConstant(0, DL, MVT::i32); + SDValue TableSetOps[] = { Chain, Sym, Callee, TableSlot }; + SDValue TableSet = DAG.getMemIntrinsicNode(WebAssemblyISD::TABLE_SET_FUNCREF, + DL, + DAG.getVTList(MVT::Other), + TableSetOps, + MVT::funcref, + // Machine Mem Operand args + MachinePointerInfo(/*funcref addrspace*/3), + CLI.CB->getCalledOperand()->getPointerAlignment(DAG.getDataLayout()), + MachineMemOperand::MOStore); + + Ops[0] = TableSet; // The new chain is the TableSet itself + } + if (CLI.IsTailCall) { // ret_calls do not return values to the current frame SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); @@ -1051,8 +1098,7 @@ for (size_t I = 0; I < Ins.size(); ++I) InVals.push_back(Res.getValue(I)); - - // Return the chain + return Res.getValue(Ins.size()); } @@ -1244,9 +1290,88 @@ 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() != 2) + return false; + + return true; +} + +bool WebAssemblyTargetLowering::isFuncrefGlobal(SDValue Op) const { + const GlobalAddressSDNode *GA = dyn_cast(Op); + if (!GA || GA->getAddressSpace() != 4) + return false; + + return true; +} + +bool WebAssemblyTargetLowering::isFuncref(const Value *Op) const { + const Type *Ty = Op->getType(); + + return isa(Ty) && Ty->getPointerAddressSpace() == 3; +} + +SDValue WebAssemblyTargetLowering::LowerStore(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + + 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) && !isFuncrefGlobal(Base)) + return Op; + + EVT VT = isExternrefGlobal(Base) ? MVT::externref : MVT::funcref; + + SDVTList Tys = DAG.getVTList(VT); + 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); + + LoadSDNode *LN = cast(Op.getNode()); + const SDValue &Base = LN->getBasePtr(); + + // Check Base is a Global Address + if (!isExternrefGlobal(Base) && !isFuncrefGlobal(Base)) + return Op; + + // Check Offset if undef + const SDValue &Offset = LN->getOffset(); + if (!Offset->isUndef()) + return Op; + + EVT VT = isExternrefGlobal(Base) ? MVT::externref : MVT::funcref; + + SDValue GlobalGet = DAG.getMemIntrinsicNode( + WebAssemblyISD::GLOBAL_GET, DL, DAG.getVTList(VT), + {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); @@ -1365,8 +1490,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()) { @@ -2017,3 +2140,23 @@ return performVectorWidenCombine(N, DCI); } } + +bool +WebAssemblyTargetLowering::splitValueIntoRegisterParts( + SelectionDAG &DAG, const SDLoc &DL, SDValue Val, SDValue *Parts, + unsigned NumParts, MVT PartVT, Optional CC) const { + if (PartVT.getSizeInBits() == 0) { + Parts[0] = Val; + return true; + } + return false; +} + +/// Return the correct alignment for the current calling convention. +Align WebAssemblyTargetLowering::getABIAlignmentForCallingConv(Type *ArgTy, + DataLayout DL) const { + if (ArgTy->isWasm_ReferenceTypeTy()) + return Align(1); + + return DL.getABITypeAlign(ArgTy); +} 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,28 @@ 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]>; +def : Pat<(funcref (wasm_global_get (WebAssemblywrapper tglobaladdr:$addr))), + (GLOBAL_GET_FUNCREF 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 tglobaladdr:$addr, EXTERNREF:$v)>, + Requires<[HasReferenceTypes]>; +def : Pat<(wasm_global_set funcref:$v, (WebAssemblywrapper tglobaladdr:$addr)), + (GLOBAL_SET_FUNCREF tglobaladdr:$addr, FUNCREF:$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/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -11,8 +11,8 @@ /// Instructions that handle tables //===----------------------------------------------------------------------===// - multiclass TABLE { + let mayLoad = 1 in defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table), (outs), (ins table32_op:$table), [], @@ -20,6 +20,7 @@ "table.get\t$table", 0x25>; + let mayStore = 1 in defm TABLE_SET_#rt : I<(outs), (ins table32_op:$table, rt:$val, I32:$i), (outs), (ins table32_op:$table), [], @@ -46,6 +47,13 @@ defm "" : TABLE, Requires<[HasReferenceTypes]>; defm "" : TABLE, Requires<[HasReferenceTypes]>; +def wasm_table_set_t : SDTypeProfile<0, 3, []>; +def wasm_table_set : SDNode<"WebAssemblyISD::TABLE_SET_FUNCREF", wasm_table_set_t, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; +def : Pat<(wasm_table_set i32:$table, funcref:$f, i32:$idx), + (TABLE_SET_FUNCREF i32:$table, funcref:$f, i32:$idx)>, + Requires<[HasReferenceTypes]>; + defm TABLE_SIZE : I<(outs I32:$sz), (ins table32_op:$table), (outs), (ins table32_op:$table), [], diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMCInstLower.cpp @@ -198,6 +198,10 @@ return wasm::ValType::F64; if (RC == &WebAssembly::V128RegClass) return wasm::ValType::V128; + if (RC == &WebAssembly::EXTERNREFRegClass) + return wasm::ValType::EXTERNREF; + if (RC == &WebAssembly::FUNCREFRegClass) + return wasm::ValType::FUNCREF; llvm_unreachable("Unexpected register class"); } 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 @@ -112,6 +112,11 @@ return *RM; } +// Check for reference types in Feature String, in order to extend target description string +static bool hasReferenceTypes(const StringRef &FS) { + return FS.find("+reference-types") != StringRef::npos; +} + /// Create an WebAssembly architecture model. /// WebAssemblyTargetMachine::WebAssemblyTargetMachine( @@ -119,8 +124,11 @@ 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() + ? (hasReferenceTypes(FS) ? "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:3" : + "e-m:e-p:64:64-i64:64-n32:64-S128") + : (hasReferenceTypes(FS) ? "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:3" : + "e-m:e-p:32:32-i64:64-n32:64-S128"), TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT), getEffectiveCodeModel(CM, CodeModel::Large), OL), TLOF(new WebAssemblyTargetObjectFile()) { diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.h @@ -48,6 +48,12 @@ getOrCreateFunctionTableSymbol(MCContext &Ctx, const WebAssemblySubtarget *Subtarget); +/// Returns the __funcref_call_table, for use in funcref calls when lowered to +/// table.set + call_indirect. +MCSymbolWasm * +getOrCreateFuncrefCallTableSymbol(MCContext &Ctx, + const WebAssemblySubtarget *Subtarget); + /// Find a catch instruction from an EH pad. Returns null if no catch /// instruction found or the catch is in an invalid location. MachineInstr *findCatch(MachineBasicBlock *EHPad); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyUtilities.cpp @@ -97,9 +97,9 @@ } } -MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol( - MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { - StringRef Name = "__indirect_function_table"; +// Helper function +MCSymbolWasm *getOrCreateTableSymbol(StringRef Name, MCContext &Ctx, + const WebAssemblySubtarget *Subtarget) { MCSymbolWasm *Sym = cast_or_null(Ctx.lookupSymbol(Name)); if (Sym) { if (!Sym->isFunctionTable()) @@ -116,6 +116,23 @@ return Sym; } +MCSymbolWasm *WebAssembly::getOrCreateFunctionTableSymbol( + MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { + StringRef Name = "__indirect_function_table"; + return getOrCreateTableSymbol(Name, Ctx, Subtarget); +} + +MCSymbolWasm *WebAssembly::getOrCreateFuncrefCallTableSymbol( + MCContext &Ctx, const WebAssemblySubtarget *Subtarget) { + StringRef Name = "__funcref_call_table"; + MCSymbolWasm *Table = getOrCreateTableSymbol(Name, Ctx, Subtarget); + + // Setting Comdat ensure only one table is left after linking when multiple + // modules define the table. + Table->setComdat(true); + return Table; +} + // Find a catch instruction from an EH pad. MachineInstr *WebAssembly::findCatch(MachineBasicBlock *EHPad) { assert(EHPad->isEHPad()); diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalget.ll b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-globalget.ll @@ -0,0 +1,19 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(1)* ;; addrspace 1 is nonintegral + +@externref_global = local_unnamed_addr addrspace(2) global %externref undef + +define %externref @return_externref_global() { + ;; this generates a global.get of @externref_global + %ref = load %externref, %externref addrspace(2)* @externref_global + ret %externref %ref +} + +; CHECK-LABEL: return_externref_global: +; CHECK-NEXT: functype return_externref_global () -> (externref) +; CHECK-NEXT: global.get externref_global +; CHECK-NEXT: end_function + +; CHECK: .globl externref_global diff --git a/llvm/test/CodeGen/WebAssembly/externref-globalset.ll b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-globalset.ll @@ -0,0 +1,20 @@ +; RUN: llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(1)* ;; addrspace 1 is nonintegral + +@externref_global = local_unnamed_addr addrspace(2) global %externref undef + +define void @set_externref_global(%externref %g) { + ;; this generates a global.set of @externref.global + store %externref %g, %externref addrspace(2)* @externref_global + ret void +} + +; CHECK-LABEL: set_externref_global: +; CHECK-NEXT: functype set_externref_global (externref) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: global.set externref_global +; CHECK-NEXT: end_function + +; CHECK: .globl externref_global diff --git a/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-inttoptr.ll @@ -0,0 +1,11 @@ +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR + +%extern = type opaque +%externref = type %extern addrspace(1)* + +define %externref @int_to_externref(i32 %i) { + %ref = inttoptr i32 %i to %externref + ret %externref %ref +} + +; CHECK-ERROR: inttoptr not supported for non-integral pointers \ No newline at end of file diff --git a/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-ptrtoint.ll @@ -0,0 +1,11 @@ +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR + +%extern = type opaque +%externref = type %extern addrspace(1)* + +define i32 @externref_to_int(%externref %ref) { + %i = ptrtoint %externref %ref to i32 + ret i32 %i +} + +; CHECK-ERROR: ptrtoint not supported for non-integral pointers \ No newline at end of file diff --git a/llvm/test/CodeGen/WebAssembly/externref-undef.ll b/llvm/test/CodeGen/WebAssembly/externref-undef.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-undef.ll @@ -0,0 +1,17 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%extern = type opaque +%externref = type %extern addrspace(1)* ;; addrspace 1 is nonintegral + +@externref_global = local_unnamed_addr addrspace(2) global %externref undef + +define %extern @return_extern_undef() { + ret %extern undef +} + +; CHECK-LABEL: return_extern_undef: +; CHECK-NEXT: functype return_extern_undef () -> () +; CHECK-NEXT: end_function + +; CHECK: .globl externref_global + diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-load.ll @@ -0,0 +1,11 @@ +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR + +%extern = type opaque +%externref = type %extern addrspace(1)* + +define void @load_extern(%externref %ref) { + %e = load %extern, %externref %ref + ret void +} + +; CHECK-ERROR: error: loading unsized types is not allowed \ No newline at end of file diff --git a/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref-unsized-store.ll @@ -0,0 +1,11 @@ +; RUN: not llc --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types < %s 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR + +%extern = type opaque +%externref = type %extern addrspace(1)* + +define void @store_extern(%externref %ref) { + store %extern undef, %externref %ref + ret void +} + +; CHECK-ERROR: error: storing unsized types is not allowed \ No newline at end of file diff --git a/llvm/test/CodeGen/WebAssembly/funcref-call.ll b/llvm/test/CodeGen/WebAssembly/funcref-call.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-call.ll @@ -0,0 +1,21 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%func = type void () +%funcref = type %func addrspace(3)* ;; addrspace 3 is nonintegral + +define void @call_funcref(%funcref %ref) { + call addrspace(3) void %ref() + ret void +} + +; CHECK-LABEL: call_funcref: +; CHECK-NEXT: functype call_funcref (funcref) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i32.const 0 +; CHECK-NEXT: table.set __funcref_call_table +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: call_indirect __funcref_call_table, () -> () +; CHECK-NEXT: end_function + +; CHECK: .tabletype __funcref_call_table, funcref + diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalget.ll @@ -0,0 +1,19 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%func = type opaque +%funcref = type %func addrspace(3)* ;; addrspace 3 is nonintegral + +@funcref_global = local_unnamed_addr addrspace(4) global %funcref undef + +define %funcref @return_funcref_global() { + ;; this generates a global.get of @funcref_global + %ref = load %funcref, %funcref addrspace(4)* @funcref_global + ret %funcref %ref +} + +; CHECK-LABEL: return_funcref_global: +; CHECK-NEXT: .functype return_funcref_global () -> (funcref) +; CHECK-NEXT: global.get funcref_global +; CHECK-NEXT: end_function + +; CHECK: .globl funcref_global diff --git a/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/funcref-globalset.ll @@ -0,0 +1,20 @@ +; RUN: llc < %s --mtriple=wasm32-unknown-unknown -asm-verbose=false -mattr=+reference-types | FileCheck %s + +%func = type opaque +%funcref = type %func addrspace(3)* ;; addrspace 3 is nonintegral + +@funcref_global = local_unnamed_addr addrspace(4) global %funcref undef + +define void @set_funcref_global(%funcref %g) { + ;; this generates a global.set of @funcref_global + store %funcref %g, %funcref addrspace(4)* @funcref_global + ret void +} + +; CHECK-LABEL: set_funcref_global: +; CHECK-NEXT: functype set_funcref_global (funcref) -> () +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: global.set funcref_global +; CHECK-NEXT: end_function + +; CHECK: .globl funcref_global diff --git a/llvm/tools/llvm-c-test/echo.cpp b/llvm/tools/llvm-c-test/echo.cpp --- a/llvm/tools/llvm-c-test/echo.cpp +++ b/llvm/tools/llvm-c-test/echo.cpp @@ -153,6 +153,10 @@ return LLVMX86AMXTypeInContext(Ctx); case LLVMX86_MMXTypeKind: return LLVMX86MMXTypeInContext(Ctx); + case LLVMWasm_FuncrefTypeKind: + return LLVMWasmFuncrefTypeInContext(Ctx); + case LLVMWasm_ExternrefTypeKind: + return LLVMWasmExternrefTypeInContext(Ctx); case LLVMTokenTypeKind: return LLVMTokenTypeInContext(Ctx); }