Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCTargetDesc.h @@ -59,6 +59,7 @@ DotResult = UINT64_MAX - 1, ///< .result DotLocal = UINT64_MAX - 2, ///< .local DotEndFunc = UINT64_MAX - 3, ///< .endfunc + DotIndIdx = UINT64_MAX - 4, /// < .indidx }; } // end namespace WebAssembly @@ -123,7 +124,8 @@ case WebAssembly::STORE_I64: case WebAssembly::STORE_F64: return 3; - default: llvm_unreachable("Only loads and stores have p2align values"); + default: + llvm_unreachable("Only loads and stores have p2align values"); } } Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.h @@ -43,6 +43,8 @@ size_t NumResults) { llvm_unreachable("emitIndirectFunctionType not implemented"); } + /// .indidx + virtual void emitIndIdx(const MCExpr *Value) = 0; }; /// This part is for ascii assembly output @@ -59,6 +61,7 @@ void emitIndirectFunctionType(StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) override; + void emitIndIdx(const MCExpr *Value) override; }; /// This part is for ELF object output @@ -70,6 +73,7 @@ void emitResult(ArrayRef Types) override; void emitLocal(ArrayRef Types) override; void emitEndFunc() override; + void emitIndIdx(const MCExpr *Value) override; }; } // end namespace llvm Index: lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp =================================================================== --- lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp +++ lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp @@ -67,13 +67,18 @@ void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType( StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) { OS << "\t.functype\t" << name; - if (NumResults == 0) OS << ", void"; + if (NumResults == 0) + OS << ", void"; for (auto Ty : SignatureVTs) { OS << ", " << WebAssembly::TypeToString(Ty); } OS << "\n"; } +void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) { + OS << "\t.indidx \t" << *Value << '\n'; +} + // FIXME: What follows is not the real binary encoding. static void EncodeTypes(MCStreamer &Streamer, ArrayRef Types) { @@ -100,3 +105,8 @@ void WebAssemblyTargetELFStreamer::emitEndFunc() { Streamer.EmitIntValue(WebAssembly::DotEndFunc, sizeof(uint64_t)); } + +void WebAssemblyTargetELFStreamer::emitIndIdx(const MCExpr *Value) { + Streamer.EmitIntValue(WebAssembly::DotIndIdx, sizeof(uint64_t)); + Streamer.EmitValue(Value, sizeof(uint64_t)); +} Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -183,6 +183,14 @@ SmallVector ResultVTs; const Function &F(*MF->getFunction()); + + // Emit the function index. + if (MDNode *Idx = F.getMetadata("wasm.index")) { + assert(Idx->getNumOperands() == 1); + + getTargetStreamer()->emitIndIdx(AsmPrinter::lowerConstant(cast(Idx->getOperand(0))->getValue())); + } + ComputeLegalValueVTs(F, TM, F.getReturnType(), ResultVTs); // If the return type needs to be legalized it will get converted into Index: lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- lib/Transforms/IPO/LowerTypeTests.cpp +++ lib/Transforms/IPO/LowerTypeTests.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "llvm/Transforms/IPO/LowerTypeTests.h" -#include "llvm/Transforms/IPO.h" #include "llvm/ADT/EquivalenceClasses.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/Triple.h" @@ -30,6 +29,7 @@ #include "llvm/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" using namespace llvm; @@ -79,8 +79,7 @@ if (!Result) return false; COffset += APOffset.getZExtValue(); - return containsValue(DL, GlobalLayout, GEP->getPointerOperand(), - COffset); + return containsValue(DL, GlobalLayout, GEP->getPointerOperand(), COffset); } if (auto Op = dyn_cast(V)) { @@ -222,6 +221,9 @@ IntegerType *Int64Ty; IntegerType *IntPtrTy; + // Indirect function call index assignment counter for WebAssembly + uint64_t IndirectIndex; + // Mapping from type identifiers to the call sites that test them. DenseMap> TypeTestCallSites; @@ -250,6 +252,10 @@ void verifyTypeMDNode(GlobalObject *GO, MDNode *Type); void buildBitSetsFromFunctions(ArrayRef TypeIds, ArrayRef Functions); + void buildBitSetsFromFunctionsX86(ArrayRef TypeIds, + ArrayRef Functions); + void buildBitSetsFromFunctionsWASM(ArrayRef TypeIds, + ArrayRef Functions); void buildBitSetsFromDisjointSet(ArrayRef TypeIds, ArrayRef Globals); bool lower(); @@ -282,6 +288,8 @@ Int64Ty = Type::getInt64Ty(M->getContext()); IntPtrTy = DL.getIntPtrType(M->getContext(), 0); + IndirectIndex = 0; + TypeTestCallSites.clear(); return false; @@ -290,8 +298,7 @@ /// Build a bit set for TypeId using the object layouts in /// GlobalLayout. BitSetInfo LowerTypeTests::buildBitSet( - Metadata *TypeId, - const DenseMap &GlobalLayout) { + Metadata *TypeId, const DenseMap &GlobalLayout) { BitSetBuilder BSB; // Compute the byte offset of each address associated with this type @@ -304,8 +311,9 @@ if (Type->getOperand(1) != TypeId) continue; uint64_t Offset = - cast(cast(Type->getOperand(0)) - ->getValue())->getZExtValue(); + cast( + cast(Type->getOperand(0))->getValue()) + ->getZExtValue(); BSB.addOffset(GlobalAndOffset.second + Offset); } } @@ -334,8 +342,8 @@ // we know the offset and mask to use. auto ByteArrayGlobal = new GlobalVariable( *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr); - auto MaskGlobal = new GlobalVariable( - *M, Int8Ty, /*isConstant=*/true, GlobalValue::PrivateLinkage, nullptr); + auto MaskGlobal = new GlobalVariable(*M, Int8Ty, /*isConstant=*/true, + GlobalValue::PrivateLinkage, nullptr); ByteArrayInfos.emplace_back(); ByteArrayInfo *BAI = &ByteArrayInfos.back(); @@ -611,8 +619,7 @@ void LowerTypeTests::verifyTypeMDNode(GlobalObject *GO, MDNode *Type) { if (Type->getNumOperands() != 2) - report_fatal_error( - "All operands of type metadata must have 2 elements"); + report_fatal_error("All operands of type metadata must have 2 elements"); if (GO->isThreadLocal()) report_fatal_error("Bit set element may not be thread-local"); @@ -635,9 +642,6 @@ static const unsigned kX86JumpTableEntrySize = 8; unsigned LowerTypeTests::getJumpTableEntrySize() { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - return kX86JumpTableEntrySize; } @@ -648,9 +652,6 @@ Constant *LowerTypeTests::createJumpTableEntry(GlobalObject *Src, Function *Dest, unsigned Distance) { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - const unsigned kJmpPCRel32Code = 0xe9; const unsigned kInt3Code = 0xcc; @@ -675,9 +676,6 @@ } Type *LowerTypeTests::getJumpTableEntryType() { - if (Arch != Triple::x86 && Arch != Triple::x86_64) - report_fatal_error("Unsupported architecture for jump tables"); - return StructType::get(M->getContext(), {Int8Ty, Int32Ty, Int8Ty, Int8Ty, Int8Ty}, /*Packed=*/true); @@ -687,6 +685,17 @@ /// for the functions, build the bit sets and lower the llvm.type.test calls. void LowerTypeTests::buildBitSetsFromFunctions(ArrayRef TypeIds, ArrayRef Functions) { + if (Arch == Triple::x86 || Arch == Triple::x86_64) { + buildBitSetsFromFunctionsX86(TypeIds, Functions); + } else if (Arch == Triple::wasm32 || Arch == Triple::wasm64) { + buildBitSetsFromFunctionsWASM(TypeIds, Functions); + } else { + report_fatal_error("Unsupported architecture for jump tables"); + } +} + +void LowerTypeTests::buildBitSetsFromFunctionsX86( + ArrayRef TypeIds, ArrayRef Functions) { // Unlike the global bitset builder, the function bitset builder cannot // re-arrange functions in a particular order and base its calculations on the // layout of the functions' entry points, as we have no idea how large a @@ -818,6 +827,32 @@ ConstantArray::get(JumpTableType, JumpTableEntries)); } +// Assign a dummy layout based using an incrementing counter, and store this as +// metadata. During generation of the indexed indirect function call table, the +// backend will ensure that the appropriate indexes are used. +void LowerTypeTests::buildBitSetsFromFunctionsWASM( + ArrayRef TypeIds, ArrayRef Functions) { + assert(!Functions.empty()); + + // Build a dummy layout. + DenseMap GlobalLayout; + for (Function *F : Functions) + GlobalLayout[F] = IndirectIndex++; + + // Pass a NULL pointer as the subtracted "jump table" offset. + lowerTypeTestCalls(TypeIds, + ConstantPointerNull::get(cast(Int32PtrTy)), + GlobalLayout); + + // Generate metadata for the indirect function indexes. + for (const auto &P : GlobalLayout) { + MDNode *MD = MDNode::get(P.first->getContext(), + ArrayRef(ConstantAsMetadata::get( + ConstantInt::get(Int64Ty, P.second)))); + cast(P.first)->setMetadata("wasm.index", MD); + } +} + void LowerTypeTests::buildBitSetsFromDisjointSet( ArrayRef TypeIds, ArrayRef Globals) { llvm::DenseMap TypeIdIndices; @@ -923,8 +958,7 @@ auto BitSetMDVal = dyn_cast(CI->getArgOperand(1)); if (!BitSetMDVal) - report_fatal_error( - "Second argument of llvm.type.test must be metadata"); + report_fatal_error("Second argument of llvm.type.test must be metadata"); auto BitSet = BitSetMDVal->getMetadata(); // Add the call site to the list of call sites for this type identifier. We @@ -962,7 +996,8 @@ for (GlobalClassesTy::iterator I = GlobalClasses.begin(), E = GlobalClasses.end(); I != E; ++I) { - if (!I->isLeader()) continue; + if (!I->isLeader()) + continue; ++NumTypeIdDisjointSets; unsigned MaxIndex = 0;