diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,17 @@ +default: + image: debian:sid + before_script: + - apt-get update + - apt-get install -y git gcc cmake wget unzip g++ python libxml2-dev ninja-build python ruby python3-pip python3-psutil libz3-dev valgrind + +build-llvm: + tags: + - linux + - x86_64 + script: + - mkdir build + - cd build + - cmake -G 'Unix Makefiles' -DLLVM_TARGETS_TO_BUILD='X86;WebAssembly' -DLLVM_ENABLE_PROJECTS="clang;lld" -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=On -DLLVM_BUILD_32_BITS=Off -DLLVM_ENABLE_BINDINGS=Off ../llvm/ + - make -j$(nproc) -l$(nproc) + - make -j$(nproc) -l$(nproc) check-llvm-unit + - make -j$(nproc) -l$(nproc) check-llvm diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -38,6 +38,7 @@ bool HasMutableGlobals = false; bool HasMultivalue = false; bool HasTailCall = false; + bool HasReferenceTypes = false; public: explicit WebAssemblyTargetInfo(const llvm::Triple &T, const TargetOptions &) @@ -69,6 +70,7 @@ initFeatureMap(llvm::StringMap &Features, DiagnosticsEngine &Diags, StringRef CPU, const std::vector &FeaturesVec) const override; + bool hasFeature(StringRef Feature) const final; bool handleTargetFeatures(std::vector &Features, @@ -121,7 +123,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"); } protected: diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -40,6 +40,7 @@ .Case("nontrapping-fptoint", HasNontrappingFPToInt) .Case("sign-ext", HasSignExt) .Case("exception-handling", HasExceptionHandling) + .Case("reference-types", HasReferenceTypes) .Case("bulk-memory", HasBulkMemory) .Case("atomics", HasAtomics) .Case("mutable-globals", HasMutableGlobals) @@ -70,6 +71,8 @@ Builder.defineMacro("__wasm_sign_ext__"); if (HasExceptionHandling) Builder.defineMacro("__wasm_exception_handling__"); + if (HasReferenceTypes) + Builder.defineMacro("__wasm_reference_types__"); if (HasBulkMemory) Builder.defineMacro("__wasm_bulk_memory__"); if (HasAtomics) @@ -116,6 +119,8 @@ Features["sign-ext"] = true; if (HasExceptionHandling) Features["exception-handling"] = true; + if (HasReferenceTypes) + Features["reference-types"] = true; if (HasBulkMemory) Features["bulk-memory"] = true; if (HasAtomics) @@ -173,6 +178,15 @@ HasExceptionHandling = false; continue; } + if (Feature == "+reference-types") { + HasReferenceTypes = true; + resetDataLayout("e-m:e-p:32:32-i64:64-n32:64-S128-ni:256"); + continue; + } + if (Feature == "-reference-types") { + HasReferenceTypes = false; + continue; + } if (Feature == "+bulk-memory") { HasBulkMemory = true; continue; diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -243,6 +243,12 @@ CC1Args.push_back("-target-feature"); CC1Args.push_back("+reference-types"); } +#if 0 + if (DriverArgs.getLastArg(options::OPT_mreference_types)) { + CC1Args.push_back("-target-feature"); + CC1Args.push_back("+reference-types"); + } +#endif } ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const { diff --git a/lld/wasm/WriterUtils.cpp b/lld/wasm/WriterUtils.cpp --- a/lld/wasm/WriterUtils.cpp +++ b/lld/wasm/WriterUtils.cpp @@ -30,6 +30,10 @@ return "f64"; case ValType::V128: return "v128"; + case ValType::FUNCREF: + return "funcref"; + case ValType::EXTERNREF: + return "externref"; case ValType::EXNREF: return "exnref"; } diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -227,6 +227,7 @@ WASM_TYPE_F64 = 0x7C, WASM_TYPE_V128 = 0x7B, WASM_TYPE_FUNCREF = 0x70, + WASM_TYPE_EXTERNREF = 0x6F, WASM_TYPE_EXNREF = 0x68, WASM_TYPE_FUNC = 0x60, WASM_TYPE_NORESULT = 0x40, // for blocks with no result values @@ -315,6 +316,7 @@ WASM_SYMBOL_TYPE_GLOBAL = 0x2, WASM_SYMBOL_TYPE_SECTION = 0x3, WASM_SYMBOL_TYPE_EVENT = 0x4, + WASM_SYMBOL_TYPE_TABLE = 0x5, }; // Kinds of event attributes. @@ -350,6 +352,8 @@ F32 = WASM_TYPE_F32, F64 = WASM_TYPE_F64, V128 = WASM_TYPE_V128, + FUNCREF = WASM_TYPE_FUNCREF, + EXTERNREF = WASM_TYPE_EXTERNREF, EXNREF = WASM_TYPE_EXNREF, }; diff --git a/llvm/include/llvm/BinaryFormat/WasmRelocs.def b/llvm/include/llvm/BinaryFormat/WasmRelocs.def --- a/llvm/include/llvm/BinaryFormat/WasmRelocs.def +++ b/llvm/include/llvm/BinaryFormat/WasmRelocs.def @@ -15,3 +15,4 @@ WASM_RELOC(R_WASM_EVENT_INDEX_LEB, 10) WASM_RELOC(R_WASM_MEMORY_ADDR_REL_SLEB, 11) WASM_RELOC(R_WASM_TABLE_INDEX_REL_SLEB, 12) +WASM_RELOC(R_WASM_TABLE_INDEX_LEB, 13) diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -254,7 +254,7 @@ /// Return the in-memory pointer type for the given address space, defaults to /// the pointer type from the data layout. FIXME: The default needs to be /// removed once all the code is updated. - MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const { + virtual MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const { return MVT::getIntegerVT(DL.getPointerSizeInBits(AS)); } 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 @@ -166,6 +166,7 @@ def isVoid : ValueType<0 , 132>; // Produces no value def untyped: ValueType<8 , 133>; // Produces an untyped value def exnref: ValueType<0, 134>; // WebAssembly's exnref type +def externref: ValueType<0, 135>; // WebAssembly's externref type def token : ValueType<0 , 248>; // TokenTy def MetadataVT: ValueType<0, 249>; // Metadata diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h --- a/llvm/include/llvm/MC/MCExpr.h +++ b/llvm/include/llvm/MC/MCExpr.h @@ -290,9 +290,10 @@ VK_Hexagon_IE, VK_Hexagon_IE_GOT, - VK_WASM_TYPEINDEX, // Reference to a symbol's type (signature) - VK_WASM_MBREL, // Memory address relative to memory base - VK_WASM_TBREL, // Table index relative to table bare + VK_WASM_TYPEINDEX, // Reference to a symbol's type (signature) + VK_WASM_TABLEINDEX, // Reference to a table + VK_WASM_MBREL, // Memory address relative to memory base + VK_WASM_TBREL, // Table index relative to table bare VK_AMDGPU_GOTPCREL32_LO, // symbol@gotpcrel32@lo VK_AMDGPU_GOTPCREL32_HI, // symbol@gotpcrel32@hi diff --git a/llvm/include/llvm/MC/MCSymbolWasm.h b/llvm/include/llvm/MC/MCSymbolWasm.h --- a/llvm/include/llvm/MC/MCSymbolWasm.h +++ b/llvm/include/llvm/MC/MCSymbolWasm.h @@ -45,6 +45,7 @@ bool isGlobal() const { return Type == wasm::WASM_SYMBOL_TYPE_GLOBAL; } bool isSection() const { return Type == wasm::WASM_SYMBOL_TYPE_SECTION; } bool isEvent() const { return Type == wasm::WASM_SYMBOL_TYPE_EVENT; } + bool isTable() const { return Type == wasm::WASM_SYMBOL_TYPE_TABLE; } wasm::WasmSymbolType getType() const { return Type; } void setType(wasm::WasmSymbolType type) { Type = type; } diff --git a/llvm/include/llvm/Object/Wasm.h b/llvm/include/llvm/Object/Wasm.h --- a/llvm/include/llvm/Object/Wasm.h +++ b/llvm/include/llvm/Object/Wasm.h @@ -63,6 +63,8 @@ bool isTypeEvent() const { return Info.Kind == wasm::WASM_SYMBOL_TYPE_EVENT; } + bool isTypeTable() const { return Info.Kind == wasm::WASM_SYMBOL_TYPE_TABLE; } + bool isDefined() const { return !isUndefined(); } bool isUndefined() const { @@ -217,9 +219,12 @@ bool isDefinedGlobalIndex(uint32_t Index) const; bool isValidEventIndex(uint32_t Index) const; bool isDefinedEventIndex(uint32_t Index) const; + bool isValidTableIndex(uint32_t Index) const; + bool isDefinedTableIndex(uint32_t Index) const; bool isValidFunctionSymbol(uint32_t Index) const; bool isValidGlobalSymbol(uint32_t Index) const; bool isValidEventSymbol(uint32_t Index) const; + bool isValidTableSymbol(uint32_t Index) const; bool isValidDataSymbol(uint32_t Index) const; bool isValidSectionSymbol(uint32_t Index) const; wasm::WasmFunction &getDefinedFunction(uint32_t Index); @@ -285,6 +290,7 @@ uint32_t NumImportedGlobals = 0; uint32_t NumImportedFunctions = 0; uint32_t NumImportedEvents = 0; + uint32_t NumImportedTables = 0; uint32_t CodeSection = 0; uint32_t DataSection = 0; uint32_t GlobalSection = 0; diff --git a/llvm/include/llvm/Support/MachineValueType.h b/llvm/include/llvm/Support/MachineValueType.h --- a/llvm/include/llvm/Support/MachineValueType.h +++ b/llvm/include/llvm/Support/MachineValueType.h @@ -218,9 +218,10 @@ // will be determined by the opcode. exnref = 134, // WebAssembly's exnref type + externref = 135, // WebAssembly's externref type FIRST_VALUETYPE = 1, // This is always the beginning of the list. - LAST_VALUETYPE = 135, // This always remains at the end of the list. + LAST_VALUETYPE = 136, // This always remains at the end of the list. // This is the current maximum for LAST_VALUETYPE. // MVT::MAX_ALLOWED_VALUETYPE is used for asserts and to size bit vectors @@ -824,6 +825,7 @@ case v1024f32: return TypeSize::Fixed(32768); case v2048i32: case v2048f32: return TypeSize::Fixed(65536); + case externref: case exnref: return TypeSize::Fixed(0); // opaque type } } diff --git a/llvm/lib/BinaryFormat/Wasm.cpp b/llvm/lib/BinaryFormat/Wasm.cpp --- a/llvm/lib/BinaryFormat/Wasm.cpp +++ b/llvm/lib/BinaryFormat/Wasm.cpp @@ -20,6 +20,8 @@ return "WASM_SYMBOL_TYPE_SECTION"; case wasm::WASM_SYMBOL_TYPE_EVENT: return "WASM_SYMBOL_TYPE_EVENT"; + case wasm::WASM_SYMBOL_TYPE_TABLE: + return "WASM_SYMBOL_TYPE_TABLE"; } llvm_unreachable("unknown symbol type"); } 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 @@ -1958,7 +1958,7 @@ case Intrinsic::experimental_widenable_condition: { // Give up on future widening oppurtunties so that we can fold away dead // paths and merge blocks before going into block-local instruction - // selection. + // selection. if (II->use_empty()) { II->eraseFromParent(); return true; @@ -5833,6 +5833,9 @@ EVT LoadResultVT = TLI->getValueType(*DL, Load->getType()); unsigned BitWidth = LoadResultVT.getSizeInBits(); + if (!BitWidth) + return false; + APInt DemandBits(BitWidth, 0); APInt WidestAndBits(BitWidth, 0); 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 @@ -4124,10 +4124,12 @@ Root = Chain; ChainI = 0; } - SDValue A = DAG.getNode(ISD::ADD, dl, - PtrVT, Ptr, - DAG.getConstant(Offsets[i], dl, PtrVT), - Flags); + SDValue A = Ptr; + if (Offsets[i] != 0) + A = DAG.getNode(ISD::ADD, dl, + PtrVT, Ptr, + DAG.getConstant(Offsets[i], dl, PtrVT), + Flags); auto MMOFlags = MachineMemOperand::MONone; if (isVolatile) MMOFlags |= MachineMemOperand::MOVolatile; 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 @@ -131,6 +131,7 @@ case MVT::Metadata:return "Metadata"; case MVT::Untyped: return "Untyped"; case MVT::exnref : return "exnref"; + case MVT::externref : return "externref"; } } @@ -230,91 +231,94 @@ case MVT::v2f64: return VectorType::get(Type::getDoubleTy(Context), 2); case MVT::v4f64: return VectorType::get(Type::getDoubleTy(Context), 4); case MVT::v8f64: return VectorType::get(Type::getDoubleTy(Context), 8); - case MVT::nxv1i1: + case MVT::nxv1i1: return VectorType::get(Type::getInt1Ty(Context), 1, /*Scalable=*/ true); - case MVT::nxv2i1: + case MVT::nxv2i1: return VectorType::get(Type::getInt1Ty(Context), 2, /*Scalable=*/ true); - case MVT::nxv4i1: + case MVT::nxv4i1: return VectorType::get(Type::getInt1Ty(Context), 4, /*Scalable=*/ true); - case MVT::nxv8i1: + case MVT::nxv8i1: return VectorType::get(Type::getInt1Ty(Context), 8, /*Scalable=*/ true); - case MVT::nxv16i1: + case MVT::nxv16i1: return VectorType::get(Type::getInt1Ty(Context), 16, /*Scalable=*/ true); - case MVT::nxv32i1: + case MVT::nxv32i1: return VectorType::get(Type::getInt1Ty(Context), 32, /*Scalable=*/ true); - case MVT::nxv1i8: + case MVT::nxv1i8: return VectorType::get(Type::getInt8Ty(Context), 1, /*Scalable=*/ true); - case MVT::nxv2i8: + case MVT::nxv2i8: return VectorType::get(Type::getInt8Ty(Context), 2, /*Scalable=*/ true); - case MVT::nxv4i8: + case MVT::nxv4i8: return VectorType::get(Type::getInt8Ty(Context), 4, /*Scalable=*/ true); - case MVT::nxv8i8: + case MVT::nxv8i8: return VectorType::get(Type::getInt8Ty(Context), 8, /*Scalable=*/ true); - case MVT::nxv16i8: + case MVT::nxv16i8: return VectorType::get(Type::getInt8Ty(Context), 16, /*Scalable=*/ true); - case MVT::nxv32i8: + case MVT::nxv32i8: return VectorType::get(Type::getInt8Ty(Context), 32, /*Scalable=*/ true); - case MVT::nxv1i16: + case MVT::nxv1i16: return VectorType::get(Type::getInt16Ty(Context), 1, /*Scalable=*/ true); - case MVT::nxv2i16: + case MVT::nxv2i16: return VectorType::get(Type::getInt16Ty(Context), 2, /*Scalable=*/ true); - case MVT::nxv4i16: + case MVT::nxv4i16: return VectorType::get(Type::getInt16Ty(Context), 4, /*Scalable=*/ true); - case MVT::nxv8i16: + case MVT::nxv8i16: return VectorType::get(Type::getInt16Ty(Context), 8, /*Scalable=*/ true); case MVT::nxv16i16: return VectorType::get(Type::getInt16Ty(Context), 16, /*Scalable=*/ true); case MVT::nxv32i16: return VectorType::get(Type::getInt16Ty(Context), 32, /*Scalable=*/ true); - case MVT::nxv1i32: + case MVT::nxv1i32: return VectorType::get(Type::getInt32Ty(Context), 1, /*Scalable=*/ true); - case MVT::nxv2i32: + case MVT::nxv2i32: return VectorType::get(Type::getInt32Ty(Context), 2, /*Scalable=*/ true); - case MVT::nxv4i32: + case MVT::nxv4i32: return VectorType::get(Type::getInt32Ty(Context), 4, /*Scalable=*/ true); - case MVT::nxv8i32: + case MVT::nxv8i32: return VectorType::get(Type::getInt32Ty(Context), 8, /*Scalable=*/ true); case MVT::nxv16i32: return VectorType::get(Type::getInt32Ty(Context), 16,/*Scalable=*/ true); case MVT::nxv32i32: return VectorType::get(Type::getInt32Ty(Context), 32,/*Scalable=*/ true); - case MVT::nxv1i64: + case MVT::nxv1i64: return VectorType::get(Type::getInt64Ty(Context), 1, /*Scalable=*/ true); - case MVT::nxv2i64: + case MVT::nxv2i64: return VectorType::get(Type::getInt64Ty(Context), 2, /*Scalable=*/ true); - case MVT::nxv4i64: + case MVT::nxv4i64: return VectorType::get(Type::getInt64Ty(Context), 4, /*Scalable=*/ true); - case MVT::nxv8i64: + case MVT::nxv8i64: return VectorType::get(Type::getInt64Ty(Context), 8, /*Scalable=*/ true); case MVT::nxv16i64: return VectorType::get(Type::getInt64Ty(Context), 16, /*Scalable=*/ true); case MVT::nxv32i64: return VectorType::get(Type::getInt64Ty(Context), 32, /*Scalable=*/ true); - case MVT::nxv2f16: + case MVT::nxv2f16: return VectorType::get(Type::getHalfTy(Context), 2, /*Scalable=*/ true); - case MVT::nxv4f16: + case MVT::nxv4f16: return VectorType::get(Type::getHalfTy(Context), 4, /*Scalable=*/ true); - case MVT::nxv8f16: + case MVT::nxv8f16: return VectorType::get(Type::getHalfTy(Context), 8, /*Scalable=*/ true); - case MVT::nxv1f32: + case MVT::nxv1f32: return VectorType::get(Type::getFloatTy(Context), 1, /*Scalable=*/ true); - case MVT::nxv2f32: + case MVT::nxv2f32: return VectorType::get(Type::getFloatTy(Context), 2, /*Scalable=*/ true); - case MVT::nxv4f32: + case MVT::nxv4f32: return VectorType::get(Type::getFloatTy(Context), 4, /*Scalable=*/ true); - case MVT::nxv8f32: + case MVT::nxv8f32: return VectorType::get(Type::getFloatTy(Context), 8, /*Scalable=*/ true); case MVT::nxv16f32: return VectorType::get(Type::getFloatTy(Context), 16, /*Scalable=*/ true); - case MVT::nxv1f64: + case MVT::nxv1f64: return VectorType::get(Type::getDoubleTy(Context), 1, /*Scalable=*/ true); - case MVT::nxv2f64: + case MVT::nxv2f64: return VectorType::get(Type::getDoubleTy(Context), 2, /*Scalable=*/ true); - case MVT::nxv4f64: + case MVT::nxv4f64: return VectorType::get(Type::getDoubleTy(Context), 4, /*Scalable=*/ true); - case MVT::nxv8f64: + case MVT::nxv8f64: return VectorType::get(Type::getDoubleTy(Context), 8, /*Scalable=*/ true); case MVT::Metadata: return Type::getMetadataTy(Context); + case MVT::externref: + // TODO: Fix AS for Webassembly + return PointerType::get(Type::getInt8Ty(Context), 256); } } diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -312,6 +312,7 @@ case VK_Hexagon_IE: return "IE"; case VK_Hexagon_IE_GOT: return "IEGOT"; case VK_WASM_TYPEINDEX: return "TYPEINDEX"; + case VK_WASM_TABLEINDEX: return "TABLEINDEX"; case VK_WASM_MBREL: return "MBREL"; case VK_WASM_TBREL: return "TBREL"; case VK_AMDGPU_GOTPCREL32_LO: return "gotpcrel32@lo"; @@ -431,6 +432,7 @@ .Case("hi8", VK_AVR_HI8) .Case("hlo8", VK_AVR_HLO8) .Case("typeindex", VK_WASM_TYPEINDEX) + .Case("tableindex", VK_WASM_TABLEINDEX) .Case("tbrel", VK_WASM_TBREL) .Case("mbrel", VK_WASM_MBREL) .Case("gotpcrel32@lo", VK_AMDGPU_GOTPCREL32_LO) diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -254,6 +254,7 @@ unsigned NumFunctionImports = 0; unsigned NumGlobalImports = 0; unsigned NumEventImports = 0; + unsigned NumTableImports = 0; uint32_t SectionCount = 0; // TargetObjectWriter wrappers. @@ -555,6 +556,7 @@ case wasm::R_WASM_FUNCTION_INDEX_LEB: case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_EVENT_INDEX_LEB: + case wasm::R_WASM_TABLE_INDEX_LEB: // Provisional value is function/global/event Wasm index assert(WasmIndices.count(RelEntry.Symbol) > 0 && "symbol not found in wasm index space"); return WasmIndices[RelEntry.Symbol]; @@ -652,6 +654,7 @@ case wasm::R_WASM_GLOBAL_INDEX_LEB: case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_EVENT_INDEX_LEB: + case wasm::R_WASM_TABLE_INDEX_LEB: writePatchableLEB(Stream, Value, Offset); break; case wasm::R_WASM_TABLE_INDEX_I32: @@ -945,6 +948,7 @@ case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: case wasm::WASM_SYMBOL_TYPE_EVENT: + case wasm::WASM_SYMBOL_TYPE_TABLE: encodeULEB128(Sym.ElementIndex, W.OS); if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 || (Sym.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0) @@ -1137,6 +1141,7 @@ TableImport.Kind = wasm::WASM_EXTERNAL_TABLE; TableImport.Table.ElemType = wasm::WASM_TYPE_FUNCREF; Imports.push_back(TableImport); + NumTableImports++; // Populate SignatureIndices, and Imports and WasmIndices for undefined // symbols. This must be done before populating WasmIndices for defined @@ -1191,6 +1196,15 @@ Imports.push_back(Import); assert(WasmIndices.count(&WS) == 0); WasmIndices[&WS] = NumEventImports++; + } else if (WS.isTable()) { + wasm::WasmImport Import; + Import.Module = WS.getImportModule(); + Import.Field = WS.getImportName(); + Import.Kind = wasm::WASM_EXTERNAL_TABLE; + Import.Table.ElemType = wasm::WASM_TYPE_EXTERNREF; + Imports.push_back(Import); + assert(WasmIndices.count(&WS) == 0); + WasmIndices[&WS] = NumTableImports++; } } } @@ -1397,7 +1411,15 @@ } LLVM_DEBUG(dbgs() << " -> event index: " << WasmIndices.find(&WS)->second << "\n"); - + } else if (WS.isTable()) { + if (WS.isDefined()) { + report_fatal_error("Defined tables are not supported yet"); + } else { + // An import; the index was assigned above. + assert(WasmIndices.count(&WS) > 0); + } + LLVM_DEBUG(dbgs() << " -> table index: " << WasmIndices.find(&WS)->second + << "\n"); } else { assert(WS.isSection()); } diff --git a/llvm/lib/Object/WasmObjectFile.cpp b/llvm/lib/Object/WasmObjectFile.cpp --- a/llvm/lib/Object/WasmObjectFile.cpp +++ b/llvm/lib/Object/WasmObjectFile.cpp @@ -470,9 +470,11 @@ std::vector ImportedGlobals; std::vector ImportedFunctions; std::vector ImportedEvents; + std::vector ImportedTables; ImportedGlobals.reserve(Imports.size()); ImportedFunctions.reserve(Imports.size()); ImportedEvents.reserve(Imports.size()); + ImportedTables.reserve(Imports.size()); for (auto &I : Imports) { if (I.Kind == wasm::WASM_EXTERNAL_FUNCTION) ImportedFunctions.emplace_back(&I); @@ -480,6 +482,8 @@ ImportedGlobals.emplace_back(&I); else if (I.Kind == wasm::WASM_EXTERNAL_EVENT) ImportedEvents.emplace_back(&I); + else if (I.Kind == wasm::WASM_EXTERNAL_TABLE) + ImportedTables.emplace_back(&I); } while (Count--) { @@ -609,6 +613,27 @@ break; } + case wasm::WASM_SYMBOL_TYPE_TABLE: { + Info.ElementIndex = readVaruint32(Ctx); + if (!isValidTableIndex(Info.ElementIndex) || + IsDefined != isDefinedTableIndex(Info.ElementIndex)) + return make_error("invalid table symbol index", + object_error::parse_failed); + if (IsDefined) { + Info.Name = readString(Ctx); + unsigned TableIndex = Info.ElementIndex - NumImportedTables; + } else { + wasm::WasmImport &Import = *ImportedTables[Info.ElementIndex]; + if ((Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0) + Info.Name = readString(Ctx); + else + Info.Name = Import.Field; + Info.ImportName = Import.Field; + Info.ImportModule = Import.Module; + } + break; + } + default: return make_error("Invalid symbol type", object_error::parse_failed); @@ -793,6 +818,11 @@ return make_error("Bad relocation event index", object_error::parse_failed); break; + case wasm::R_WASM_TABLE_INDEX_LEB: + if (!isValidTableSymbol(Reloc.Index)) + return make_error("Bad relocation table index", + object_error::parse_failed); + break; case wasm::R_WASM_MEMORY_ADDR_LEB: case wasm::R_WASM_MEMORY_ADDR_SLEB: case wasm::R_WASM_MEMORY_ADDR_I32: @@ -915,8 +945,10 @@ Im.Memory = readLimits(Ctx); break; case wasm::WASM_EXTERNAL_TABLE: + NumImportedTables++; Im.Table = readTable(Ctx); - if (Im.Table.ElemType != wasm::WASM_TYPE_FUNCREF) + if (Im.Table.ElemType != wasm::WASM_TYPE_FUNCREF && + Im.Table.ElemType != wasm::WASM_TYPE_EXTERNREF) return make_error("Invalid table element type", object_error::parse_failed); break; @@ -960,7 +992,9 @@ Tables.reserve(Count); while (Count--) { Tables.push_back(readTable(Ctx)); - if (Tables.back().ElemType != wasm::WASM_TYPE_FUNCREF) { + if (Tables.back().ElemType != wasm::WASM_TYPE_FUNCREF && + // TODO: Only allow externref here when reference-types is enabled? + Tables.back().ElemType != wasm::WASM_TYPE_EXTERNREF) { return make_error("Invalid table element type", object_error::parse_failed); } @@ -1085,6 +1119,14 @@ return Index >= NumImportedEvents && isValidEventIndex(Index); } +bool WasmObjectFile::isValidTableIndex(uint32_t Index) const { + return Index < NumImportedTables + Tables.size(); +} + +bool WasmObjectFile::isDefinedTableIndex(uint32_t Index) const { + return Index >= NumImportedTables && isValidTableIndex(Index); +} + bool WasmObjectFile::isValidFunctionSymbol(uint32_t Index) const { return Index < Symbols.size() && Symbols[Index].isTypeFunction(); } @@ -1097,6 +1139,10 @@ return Index < Symbols.size() && Symbols[Index].isTypeEvent(); } +bool WasmObjectFile::isValidTableSymbol(uint32_t Index) const { + return Index < Symbols.size() && Symbols[Index].isTypeTable(); +} + bool WasmObjectFile::isValidDataSymbol(uint32_t Index) const { return Index < Symbols.size() && Symbols[Index].isTypeData(); } diff --git a/llvm/lib/ObjectYAML/WasmEmitter.cpp b/llvm/lib/ObjectYAML/WasmEmitter.cpp --- a/llvm/lib/ObjectYAML/WasmEmitter.cpp +++ b/llvm/lib/ObjectYAML/WasmEmitter.cpp @@ -188,6 +188,7 @@ case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: case wasm::WASM_SYMBOL_TYPE_EVENT: + case wasm::WASM_SYMBOL_TYPE_TABLE: encodeULEB128(Info.ElementIndex, SubSection.getStream()); if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 || (Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0) diff --git a/llvm/lib/ObjectYAML/WasmYAML.cpp b/llvm/lib/ObjectYAML/WasmYAML.cpp --- a/llvm/lib/ObjectYAML/WasmYAML.cpp +++ b/llvm/lib/ObjectYAML/WasmYAML.cpp @@ -493,6 +493,8 @@ IO.mapRequired("Global", Info.ElementIndex); } else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_EVENT) { IO.mapRequired("Event", Info.ElementIndex); + } else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_TABLE) { + IO.mapRequired("Table", Info.ElementIndex); } else if (Info.Kind == wasm::WASM_SYMBOL_TYPE_DATA) { if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { IO.mapRequired("Segment", Info.DataRef.Segment); @@ -547,6 +549,7 @@ ECase(GLOBAL); ECase(SECTION); ECase(EVENT); + ECase(TABLE); #undef ECase } @@ -559,6 +562,7 @@ ECase(F64); ECase(V128); ECase(FUNCREF); + ECase(EXTERNREF); ECase(FUNC); #undef ECase } @@ -590,6 +594,7 @@ IO &IO, WasmYAML::TableType &Type) { #define ECase(X) IO.enumCase(Type, #X, wasm::WASM_TYPE_##X); ECase(FUNCREF); + ECase(EXTERNREF); #undef ECase } diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp --- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp +++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp @@ -310,6 +310,8 @@ return wasm::ValType::V128; if (Type == "exnref") return wasm::ValType::EXNREF; + if (Type == "externref") + return wasm::ValType::EXTERNREF; return Optional(); } @@ -322,6 +324,7 @@ .Case("f64", WebAssembly::BlockType::F64) .Case("v128", WebAssembly::BlockType::V128) .Case("exnref", WebAssembly::BlockType::Exnref) + .Case("externref", WebAssembly::BlockType::Externref) .Case("void", WebAssembly::BlockType::Void) .Default(WebAssembly::BlockType::Invalid); } diff --git a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp --- a/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp +++ b/llvm/lib/Target/WebAssembly/Disassembler/WebAssemblyDisassembler.cpp @@ -196,6 +196,7 @@ case WebAssembly::OPERAND_BASIC_BLOCK: case WebAssembly::OPERAND_LOCAL: case WebAssembly::OPERAND_GLOBAL: + case WebAssembly::OPERAND_TABLE: case WebAssembly::OPERAND_FUNCTION32: case WebAssembly::OPERAND_OFFSET32: case WebAssembly::OPERAND_P2ALIGN: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp @@ -306,6 +306,8 @@ return "v128"; case wasm::WASM_TYPE_FUNCREF: return "funcref"; + case wasm::WASM_TYPE_EXTERNREF: + return "externref"; case wasm::WASM_TYPE_FUNC: return "func"; case wasm::WASM_TYPE_EXNREF: diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCCodeEmitter.cpp @@ -155,6 +155,7 @@ case WebAssembly::OPERAND_SIGNATURE: case WebAssembly::OPERAND_TYPEINDEX: case WebAssembly::OPERAND_GLOBAL: + case WebAssembly::OPERAND_TABLE: case WebAssembly::OPERAND_EVENT: FixupKind = MCFixupKind(WebAssembly::fixup_uleb128_i32); break; diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -48,6 +48,8 @@ OPERAND_LOCAL, /// Global index. OPERAND_GLOBAL, + /// Table index. + OPERAND_TABLE, /// 32-bit integer immediates. OPERAND_I32IMM, /// 64-bit integer immediates. @@ -102,6 +104,9 @@ // address relative the __table_base wasm global. // Only applicable to function symbols. MO_TABLE_BASE_REL, + + // On a symbol operand this indicates that this operand is a table index. + MO_TABLE_INDEX, }; } // end namespace WebAssemblyII @@ -132,6 +137,7 @@ F64 = unsigned(wasm::ValType::F64), V128 = unsigned(wasm::ValType::V128), Exnref = unsigned(wasm::ValType::EXNREF), + Externref = unsigned(wasm::ValType::EXTERNREF), // Multivalue blocks (and other non-void blocks) are only emitted when the // blocks will never be exited and are at the ends of functions (see // WebAssemblyCFGStackify::fixEndsAtEndOfFunction). They also are never made @@ -393,6 +399,8 @@ case WebAssembly::ARGUMENT_v2f64_S: case WebAssembly::ARGUMENT_exnref: case WebAssembly::ARGUMENT_exnref_S: + case WebAssembly::ARGUMENT_externref: + case WebAssembly::ARGUMENT_externref_S: return true; default: return false; @@ -413,6 +421,8 @@ case WebAssembly::COPY_V128_S: case WebAssembly::COPY_EXNREF: case WebAssembly::COPY_EXNREF_S: + case WebAssembly::COPY_EXTERNREF: + case WebAssembly::COPY_EXTERNREF_S: return true; default: return false; @@ -433,6 +443,8 @@ case WebAssembly::TEE_V128_S: case WebAssembly::TEE_EXNREF: case WebAssembly::TEE_EXNREF_S: + case WebAssembly::TEE_EXTERNREF: + case WebAssembly::TEE_EXTERNREF_S: return true; default: return false; @@ -465,6 +477,8 @@ case WebAssembly::CALL_v2f64_S: case WebAssembly::CALL_exnref: case WebAssembly::CALL_exnref_S: + case WebAssembly::CALL_externref: + case WebAssembly::CALL_externref_S: case WebAssembly::RET_CALL: case WebAssembly::RET_CALL_S: return true; @@ -499,6 +513,8 @@ case WebAssembly::CALL_INDIRECT_v2f64_S: case WebAssembly::CALL_INDIRECT_exnref: case WebAssembly::CALL_INDIRECT_exnref_S: + case WebAssembly::CALL_INDIRECT_externref: + case WebAssembly::CALL_INDIRECT_externref_S: case WebAssembly::RET_CALL_INDIRECT: case WebAssembly::RET_CALL_INDIRECT_S: return true; @@ -542,6 +558,8 @@ case WebAssembly::CALL_v2f64_S: case WebAssembly::CALL_exnref: case WebAssembly::CALL_exnref_S: + case WebAssembly::CALL_externref: + case WebAssembly::CALL_externref_S: case WebAssembly::CALL_INDIRECT_i32: case WebAssembly::CALL_INDIRECT_i32_S: case WebAssembly::CALL_INDIRECT_i64: @@ -562,6 +580,8 @@ case WebAssembly::CALL_INDIRECT_v4f32_S: case WebAssembly::CALL_INDIRECT_v2f64: case WebAssembly::CALL_INDIRECT_v2f64_S: + case WebAssembly::CALL_INDIRECT_externref: + case WebAssembly::CALL_INDIRECT_externref_S: case WebAssembly::CALL_INDIRECT_exnref: case WebAssembly::CALL_INDIRECT_exnref_S: return 1; diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.cpp @@ -149,6 +149,8 @@ return wasm::ValType::V128; case MVT::exnref: return wasm::ValType::EXNREF; + case MVT::externref: + return wasm::ValType::EXTERNREF; default: llvm_unreachable("unexpected type"); } diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp --- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp +++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyWasmObjectWriter.cpp @@ -73,6 +73,8 @@ switch (Modifier) { case MCSymbolRefExpr::VK_GOT: return wasm::R_WASM_GLOBAL_INDEX_LEB; + case MCSymbolRefExpr::VK_WASM_TABLEINDEX: + return wasm::R_WASM_TABLE_INDEX_LEB; case MCSymbolRefExpr::VK_WASM_TBREL: assert(SymA.isFunction()); return wasm::R_WASM_TABLE_INDEX_REL_SLEB; @@ -99,6 +101,8 @@ return wasm::R_WASM_FUNCTION_INDEX_LEB; if (SymA.isEvent()) return wasm::R_WASM_EVENT_INDEX_LEB; + if (SymA.isTable()) + return wasm::R_WASM_TABLE_INDEX_LEB; return wasm::R_WASM_MEMORY_ADDR_LEB; case FK_Data_4: if (SymA.isFunction()) diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -85,4 +85,12 @@ } // end namespace llvm +namespace WebAssemblyAS { +enum : unsigned { + // The maxium value for custom address-spaces. + MAX_CUSTOM_ADDRESS = 255, + EXTERNREF_ADDRESS = 256, // Address space for externref +}; +} + #endif diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.td b/llvm/lib/Target/WebAssembly/WebAssembly.td --- a/llvm/lib/Target/WebAssembly/WebAssembly.td +++ b/llvm/lib/Target/WebAssembly/WebAssembly.td @@ -66,6 +66,10 @@ SubtargetFeature<"mutable-globals", "HasMutableGlobals", "true", "Enable mutable globals">; +def FeatureReferenceTypes : + SubtargetFeature<"reference-types", "HasReferenceTypes", "true", + "Enable referenceTypes">; + //===----------------------------------------------------------------------===// // Architectures. //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -339,6 +339,7 @@ case WebAssembly::ARGUMENT_v4f32_S: case WebAssembly::ARGUMENT_v2f64: case WebAssembly::ARGUMENT_v2f64_S: + case WebAssembly::ARGUMENT_externref: // These represent values which are live into the function entry, so there's // no instruction to emit. break; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -93,6 +93,8 @@ return WebAssembly::DROP_V128; if (RC == &WebAssembly::EXNREFRegClass) return WebAssembly::DROP_EXNREF; + if (RC == &WebAssembly::EXTERNREFRegClass) + return WebAssembly::DROP_EXTERNREF; llvm_unreachable("Unexpected register class"); } @@ -110,6 +112,8 @@ return WebAssembly::LOCAL_GET_V128; if (RC == &WebAssembly::EXNREFRegClass) return WebAssembly::LOCAL_GET_EXNREF; + if (RC == &WebAssembly::EXTERNREFRegClass) + return WebAssembly::LOCAL_GET_EXTERNREF; llvm_unreachable("Unexpected register class"); } @@ -127,6 +131,8 @@ return WebAssembly::LOCAL_SET_V128; if (RC == &WebAssembly::EXNREFRegClass) return WebAssembly::LOCAL_SET_EXNREF; + if (RC == &WebAssembly::EXNREFRegClass) + return WebAssembly::LOCAL_SET_EXTERNREF; llvm_unreachable("Unexpected register class"); } @@ -144,6 +150,8 @@ return WebAssembly::LOCAL_TEE_V128; if (RC == &WebAssembly::EXNREFRegClass) return WebAssembly::LOCAL_TEE_EXNREF; + if (RC == &WebAssembly::EXTERNREFRegClass) + return WebAssembly::LOCAL_TEE_EXTERNREF; llvm_unreachable("Unexpected register class"); } @@ -161,6 +169,8 @@ return MVT::v16i8; if (RC == &WebAssembly::EXNREFRegClass) return MVT::exnref; + if (RC == &WebAssembly::EXTERNREFRegClass) + return MVT::externref; llvm_unreachable("unrecognized register class"); } 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,6 +130,7 @@ case MVT::f32: case MVT::f64: case MVT::exnref: + case MVT::externref: return VT; case MVT::f16: return MVT::f32; @@ -706,6 +707,10 @@ Opc = WebAssembly::ARGUMENT_exnref; RC = &WebAssembly::EXNREFRegClass; break; + case MVT::externref: + Opc = WebAssembly::ARGUMENT_externref; + RC = &WebAssembly::EXTERNREFRegClass; + break; default: return false; } @@ -824,6 +829,11 @@ : WebAssembly::PCALL_INDIRECT_exnref; ResultReg = createResultReg(&WebAssembly::EXNREFRegClass); break; + case MVT::externref: + Opc = IsDirect ? WebAssembly::CALL_externref + : WebAssembly::PCALL_INDIRECT_externref; + ResultReg = createResultReg(&WebAssembly::EXTERNREFRegClass); + break; default: return false; } @@ -1329,6 +1339,7 @@ case MVT::v4f32: case MVT::v2f64: case MVT::exnref: + case MVT::externref: break; default: return false; 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 @@ -15,6 +15,7 @@ #ifndef LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H #define LLVM_LIB_TARGET_WEBASSEMBLY_WEBASSEMBLYISELLOWERING_H +#include "WebAssembly.h" #include "llvm/CodeGen/TargetLowering.h" namespace llvm { @@ -51,6 +52,18 @@ /// right decision when generating code for different targets. const WebAssemblySubtarget *Subtarget; + MVT getPointerTy(const DataLayout &DL, uint32_t AS = 0) const override { + return AS == WebAssemblyAS::EXTERNREF_ADDRESS + ? MVT::externref + : MVT::getIntegerVT(DL.getPointerSizeInBits(AS)); + } + + MVT getPointerMemTy(const DataLayout &DL, uint32_t AS = 0) const override { + return AS == WebAssemblyAS::EXTERNREF_ADDRESS + ? MVT::externref + : MVT::getIntegerVT(DL.getPointerSizeInBits(AS)); + } + AtomicExpansionKind shouldExpandAtomicRMWInIR(AtomicRMWInst *) const override; FastISel *createFastISel(FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const override; @@ -96,6 +109,7 @@ void ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const override; + bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override; const char *getClearCacheBuiltinName() const override { report_fatal_error("llvm.clear_cache is not supported on wasm"); 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 @@ -13,6 +13,7 @@ #include "WebAssemblyISelLowering.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" +#include "WebAssembly.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblySubtarget.h" #include "WebAssemblyTargetMachine.h" @@ -66,6 +67,10 @@ addRegisterClass(MVT::v2i64, &WebAssembly::V128RegClass); addRegisterClass(MVT::v2f64, &WebAssembly::V128RegClass); } + if (Subtarget->hasReferenceTypes()) { + addRegisterClass(MVT::externref, &WebAssembly::EXTERNREFRegClass); + setOperationAction(ISD::GlobalAddress, MVT::externref, Custom); + } // Compute derived properties from the register classes. computeRegisterProperties(Subtarget->getRegisterInfo()); @@ -966,6 +971,14 @@ return Chain; } +bool WebAssemblyTargetLowering::isNoopAddrSpaceCast(unsigned SrcAS, + unsigned DestAS) const { + assert(SrcAS != DestAS && "Expected different address spaces!"); + + return SrcAS <= WebAssemblyAS::MAX_CUSTOM_ADDRESS && + DestAS <= WebAssemblyAS::MAX_CUSTOM_ADDRESS; +} + void WebAssemblyTargetLowering::ReplaceNodeResults( SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const { switch (N->getOpcode()) { @@ -1111,8 +1124,9 @@ 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 (GA->getAddressSpace() != 0 && + GA->getAddressSpace() != WebAssemblyAS::EXTERNREF_ADDRESS) + fail(DL, DAG, "WebAssembly only expects the 0 or 256 (externref) address space"); unsigned OperandFlags = 0; if (isPositionIndependent()) { @@ -1144,6 +1158,10 @@ } } + if (GA->getAddressSpace() == WebAssemblyAS::EXTERNREF_ADDRESS) { + OperandFlags = WebAssemblyII::MO_TABLE_INDEX; + } + return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT, GA->getOffset(), OperandFlags)); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -60,6 +60,7 @@ defm "" : CALL; defm "" : CALL; defm "" : CALL; +defm "" : CALL; defm "" : CALL; defm "" : CALL; defm "" : CALL; @@ -142,6 +143,9 @@ def : Pat<(exnref (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), (CALL_exnref tglobaladdr:$callee)>, Requires<[HasExceptionHandling]>; +def : Pat<(externref (WebAssemblycall1 (WebAssemblywrapper tglobaladdr:$callee))), + (CALL_externref tglobaladdr:$callee)>, + Requires<[HasReferenceTypes]>; def : Pat<(WebAssemblycall0 (WebAssemblywrapper tglobaladdr:$callee)), (CALL_VOID tglobaladdr:$callee)>; def : Pat<(WebAssemblyretcall (WebAssemblywrapper tglobaladdr:$callee)), @@ -171,6 +175,9 @@ def : Pat<(exnref (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), (CALL_exnref texternalsym:$callee)>, Requires<[HasExceptionHandling]>; +def : Pat<(externref (WebAssemblycall1 (WebAssemblywrapper texternalsym:$callee))), + (CALL_externref texternalsym:$callee)>, + Requires<[HasReferenceTypes]>; def : Pat<(WebAssemblycall0 (WebAssemblywrapper texternalsym:$callee)), (CALL_VOID texternalsym:$callee)>; def : Pat<(WebAssemblyretcall (WebAssemblywrapper texternalsym:$callee)), diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -78,6 +78,8 @@ CopyOpcode = WebAssembly::COPY_V128; else if (RC == &WebAssembly::EXNREFRegClass) CopyOpcode = WebAssembly::COPY_EXNREF; + else if (RC == &WebAssembly::EXTERNREFRegClass) + CopyOpcode = WebAssembly::COPY_EXTERNREF; else llvm_unreachable("Unexpected register class"); 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 @@ -62,6 +62,10 @@ Predicate<"Subtarget->hasBulkMemory()">, AssemblerPredicate<"FeatureBulkMemory", "bulk-memory">; +def HasReferenceTypes : + Predicate<"Subtarget->hasReferenceTypes()">, + AssemblerPredicate<"FeatureReferenceTypes", "reference-types">; + //===----------------------------------------------------------------------===// // WebAssembly-specific DAG Node Types. //===----------------------------------------------------------------------===// @@ -141,6 +145,9 @@ let OperandType = "OPERAND_GLOBAL" in def global_op : Operand; +let OperandType = "OPERAND_TABLE" in +def table_op : Operand; + let OperandType = "OPERAND_I32IMM" in def i32imm_op : Operand; @@ -226,6 +233,7 @@ defm "": ARGUMENT; defm "": ARGUMENT; defm "": ARGUMENT; +defm "": ARGUMENT; // local.get and local.set are not generated by instruction selection; they // are implied by virtual register uses and defs. @@ -296,6 +304,7 @@ defm "" : LOCAL; defm "" : LOCAL, Requires<[HasSIMD128]>; defm "" : LOCAL, Requires<[HasExceptionHandling]>; +defm "" : LOCAL, Requires<[HasReferenceTypes]>; let isMoveImm = 1, isAsCheapAsAMove = 1, isReMaterializable = 1 in { defm CONST_I32 : I<(outs I32:$res), (ins i32imm_op:$imm), @@ -348,3 +357,4 @@ include "WebAssemblyInstrSIMD.td" include "WebAssemblyInstrRef.td" include "WebAssemblyInstrBulkMemory.td" +include "WebAssemblyInstrTable.td" diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrTable.td @@ -0,0 +1,12 @@ +multiclass WebAssemblyTableGet { + let mayLoad = 1, UseNamedOperandTable = 1 in + defm "": I<(outs rc:$dst), + (ins table_op: $table, I32:$offset), + (outs), (ins table_op: $table), + [], !strconcat(Name, "\t$dst, ${offset}(${table})"), + !strconcat(Name, "\t${table}"), Opcode>; +} + +defm TABLE_GET : WebAssemblyTableGet; + +def : Pat<(externref (load (WebAssemblywrapper externref:$addr))), (TABLE_GET externref:$addr, (CONST_I32 0))>; 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 @@ -64,6 +64,11 @@ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); } + unsigned TargetFlags = MO.getTargetFlags(); + if (TargetFlags == WebAssemblyII::MO_TABLE_INDEX) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); + } + return WasmSym; } @@ -90,6 +95,12 @@ return WasmSym; } + unsigned TargetFlags = MO.getTargetFlags(); + if (TargetFlags == WebAssemblyII::MO_TABLE_INDEX) { + WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TABLE); + return WasmSym; + } + SmallVector Returns; SmallVector Params; if (strcmp(Name, "__cpp_exception") == 0) { @@ -130,6 +141,9 @@ switch (TargetFlags) { case WebAssemblyII::MO_NO_FLAG: break; + case WebAssemblyII::MO_TABLE_INDEX: + Kind = MCSymbolRefExpr::VK_WASM_TABLEINDEX; + break; case WebAssemblyII::MO_GOT: Kind = MCSymbolRefExpr::VK_GOT; break; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyPeephole.cpp @@ -114,6 +114,9 @@ case WebAssembly::EXNREFRegClassID: CopyLocalOpc = WebAssembly::COPY_EXNREF; break; + case WebAssembly::EXTERNREFRegClassID: + CopyLocalOpc = WebAssembly::COPY_EXTERNREF; + break; default: llvm_unreachable("Unexpected register class for return operand"); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyRegisterInfo.td @@ -44,6 +44,7 @@ def V128_0: WebAssemblyReg<"%v128">; def EXNREF_0 : WebAssemblyReg<"%exnref.0">; +def EXTERNREF_0 : WebAssemblyReg<"%externref.0">; // The value stack "register". This is an opaque entity which serves to order // uses and defs that must remain in LIFO order. @@ -65,3 +66,4 @@ def V128 : WebAssemblyRegClass<[v4f32, v2f64, v2i64, v4i32, v16i8, v8i16], 128, (add V128_0)>; def EXNREF : WebAssemblyRegClass<[exnref], 0, (add EXNREF_0)>; +def EXTERNREF : WebAssemblyRegClass<[externref], 0, (add EXTERNREF_0)>; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h --- a/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h @@ -47,6 +47,7 @@ bool HasMultivalue = false; bool HasMutableGlobals = false; bool HasTailCall = false; + bool HasReferenceTypes = false; /// String name of used CPU. std::string CPUString; @@ -104,6 +105,7 @@ bool hasMultivalue() const { return HasMultivalue; } bool hasMutableGlobals() const { return HasMutableGlobals; } bool hasTailCall() const { return HasTailCall; } + bool hasReferenceTypes() const { return HasReferenceTypes; } /// Parses features string setting specified subtarget options. Definition of /// function is auto generated by tblgen. 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 @@ -102,15 +102,45 @@ return *RM; } +static bool HasReferenceTypes(StringRef FS) { + bool ReferenceTypes = false; + SmallVector Features; + + FS.split(Features, ',', -1, false /* KeepEmpty */); + for (auto &Feature : Features) { + if (Feature == "reference-types" || Feature == "+reference-types") + ReferenceTypes = true; + if (Feature == "-reference-types") + ReferenceTypes = false; + } + + return ReferenceTypes; +} + +static std::string computeDataLayout(const Triple &TT, StringRef FS) { + std::string Ret = "e-m:e"; // little endian, mangling elf + + if (TT.isArch64Bit()) { + Ret += "-p:64:64"; + } else { + Ret += "-p:32:32"; + } + + Ret += "-i64:64-n32:64-S128"; + + if (HasReferenceTypes(FS)) { + Ret += "-ni:256"; // externref + } + return Ret; +} + /// Create an WebAssembly architecture model. /// WebAssemblyTargetMachine::WebAssemblyTargetMachine( const Target &T, const Triple &TT, StringRef CPU, StringRef FS, 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", + : LLVMTargetMachine(T, computeDataLayout(TT, FS), TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT), getEffectiveCodeModel(CM, CodeModel::Large), OL), TLOF(new WebAssemblyTargetObjectFile()) { diff --git a/llvm/test/CodeGen/WebAssembly/externref.ll b/llvm/test/CodeGen/WebAssembly/externref.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/externref.ll @@ -0,0 +1,29 @@ +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -mattr=+reference-types | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:256" +target triple = "wasm32-unknown-unknown" + +declare i8 addrspace(256)* @test(i8 addrspace(256)*) + + +; CHECK-LABEL: call_test: +; CHECK: .functype call_test (externref) -> (externref) +define i8 addrspace(256)* @call_test(i8 addrspace(256)*) { +; CHECK: externref.call $push0=, test, $0 + %a = call i8 addrspace(256)* @test(i8 addrspace(256)* %0) + ret i8 addrspace(256)* %a +} + +; TODO: nullref? +; define i8 addrspace(256)* @null_test() { +; ret i8 addrspace(256)* null +; } + +; TODO: Loading a externref from a pointer +; @glob = external global i8 addrspace(256)*, align 4 +; define i8 addrspace(256)* @global_test() { +; %a = load i8 addrspace(256)*, i8 addrspace(256)** @glob +; ret i8 addrspace(256)* %a +; } + +; CHECK: .functype test (externref) -> (externref) diff --git a/llvm/test/CodeGen/WebAssembly/reg-argument.mir b/llvm/test/CodeGen/WebAssembly/reg-argument.mir --- a/llvm/test/CodeGen/WebAssembly/reg-argument.mir +++ b/llvm/test/CodeGen/WebAssembly/reg-argument.mir @@ -57,3 +57,14 @@ %1:exnref = ARGUMENT_exnref 0, implicit $arguments RETURN implicit-def $arguments ... +--- +name: argument_externref +# CHECK-LABEL: argument_externref +body: | + ; CHECK-LABEL: bb.0: + ; CHECK-NEXT: %1:externref = ARGUMENT_externref 0 + bb.0: + %0:i32 = CONST_I32 0, implicit-def $arguments + %1:externref = ARGUMENT_externref 0, implicit $arguments + RETURN implicit-def $arguments +... diff --git a/llvm/test/CodeGen/WebAssembly/reg-copy.mir b/llvm/test/CodeGen/WebAssembly/reg-copy.mir --- a/llvm/test/CodeGen/WebAssembly/reg-copy.mir +++ b/llvm/test/CodeGen/WebAssembly/reg-copy.mir @@ -66,3 +66,14 @@ %0:exnref = COPY %1:exnref RETURN implicit-def $arguments ... +--- +name: copy_externref +# CHECK-LABEL: copy_externref +body: | + ; CHECK-LABEL: bb.0 + ; CHECK-NEXT: %0:externref = COPY_EXTERNREF %1:externref + ; CHECK-NEXT: RETURN + bb.0: + %0:externref = COPY %1:externref + RETURN implicit-def $arguments +... diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -196,6 +196,7 @@ case MVT::iPTRAny: return "MVT::iPTRAny"; case MVT::Untyped: return "MVT::Untyped"; case MVT::exnref: return "MVT::exnref"; + case MVT::externref: return "MVT::externref"; default: llvm_unreachable("ILLEGAL VALUE TYPE!"); } }