diff options
Diffstat (limited to 'lld/wasm')
-rw-r--r-- | lld/wasm/Driver.cpp | 46 | ||||
-rw-r--r-- | lld/wasm/SymbolTable.cpp | 275 | ||||
-rw-r--r-- | lld/wasm/SymbolTable.h | 9 | ||||
-rw-r--r-- | lld/wasm/Symbols.cpp | 8 | ||||
-rw-r--r-- | lld/wasm/Symbols.h | 2 |
5 files changed, 267 insertions, 73 deletions
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 40e88f2ac2a..d3286af258b 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -534,17 +534,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { for (auto *Arg : Args.filtered(OPT_undefined)) handleUndefined(Arg->getValue()); - // Handle the `--export <sym>` options - // This works like --undefined but also exports the symbol if its found - for (auto *Arg : Args.filtered(OPT_export)) { - Symbol *Sym = handleUndefined(Arg->getValue()); - if (Sym && Sym->isDefined()) - Sym->ForceExport = true; - else if (!Config->AllowUndefined) - error(Twine("symbol exported via --export not found: ") + - Arg->getValue()); - } - Symbol *EntrySym = nullptr; if (!Config->Relocatable) { if (!Config->Shared && !Config->Entry.empty()) { @@ -555,26 +544,47 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { error("entry symbol not defined (pass --no-entry to supress): " + Config->Entry); } - - // Make sure we have resolved all symbols. - if (!Config->AllowUndefined) - Symtab->reportRemainingUndefines(); } if (errorCount()) return; + // Handle the `--export <sym>` options + // This works like --undefined but also exports the symbol if its found + for (auto *Arg : Args.filtered(OPT_export)) + handleUndefined(Arg->getValue()); + // Do link-time optimization if given files are LLVM bitcode files. // This compiles bitcode files into real object files. Symtab->addCombinedLTOObject(); if (errorCount()) return; - // Add synthetic dummies for weak undefined functions. Must happen - // after LTO otherwise functions may not yet have signatures. - if (!Config->Relocatable) + // Resolve any variant symbols that were created due to signature + // mismatchs. + Symtab->handleSymbolVariants(); + if (errorCount()) + return; + + for (auto *Arg : Args.filtered(OPT_export)) { + Symbol *Sym = Symtab->find(Arg->getValue()); + if (Sym && Sym->isDefined()) + Sym->ForceExport = true; + else if (!Config->AllowUndefined) + error(Twine("symbol exported via --export not found: ") + + Arg->getValue()); + } + + if (!Config->Relocatable) { + // Add synthetic dummies for weak undefined functions. Must happen + // after LTO otherwise functions may not yet have signatures. Symtab->handleWeakUndefines(); + // Make sure we have resolved all symbols. + if (!Config->AllowUndefined) + Symtab->reportRemainingUndefines(); + } + if (EntrySym) EntrySym->setHidden(false); diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 35ac2c5a6da..b196d5017c9 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -26,6 +26,38 @@ using namespace lld::wasm; SymbolTable *lld::wasm::Symtab; +static char encodeValType(ValType Type) { + switch (Type) { + case ValType::I32: + return 'i'; + case ValType::I64: + return 'j'; + case ValType::F32: + return 'f'; + case ValType::F64: + return 'd'; + case ValType::V128: + return 'V'; + case ValType::EXCEPT_REF: + return 'e'; + } + llvm_unreachable("invalid wasm type"); +} + +static std::string encodeSignature(const WasmSignature &Sig) { + std::string S = ":"; + for (ValType Type : Sig.Returns) + S += encodeValType(Type); + S += ':'; + for (ValType Type : Sig.Params) + S += encodeValType(Type); + return S; +} + +static StringRef getVariantName(StringRef BaseName, const WasmSignature &Sig) { + return Saver.save(BaseName + encodeSignature(Sig)); +} + void SymbolTable::addFile(InputFile *File) { log("Processing: " + toString(File)); if (Config->Trace) @@ -81,6 +113,11 @@ Symbol *SymbolTable::find(StringRef Name) { return SymVector[It->second]; } +void SymbolTable::replace(StringRef Name, Symbol* Sym) { + auto It = SymMap.find(CachedHashStringRef(Name)); + SymVector[It->second] = Sym; +} + std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) { bool Trace = false; auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()}); @@ -123,29 +160,19 @@ static void reportTypeError(const Symbol *Existing, const InputFile *File, } // Check the type of new symbol matches that of the symbol is replacing. -// For functions this can also involve verifying that the signatures match. -static void checkFunctionType(Symbol *Existing, const InputFile *File, - const WasmSignature *NewSig) { - auto ExistingFunction = dyn_cast<FunctionSymbol>(Existing); - if (!ExistingFunction) { - reportTypeError(Existing, File, WASM_SYMBOL_TYPE_FUNCTION); - return; - } - +// Returns true if the function types match, false is there is a singature +// mismatch. +bool signatureMatches(FunctionSymbol *Existing, const WasmSignature *NewSig) { if (!NewSig) - return; + return true; - const WasmSignature *OldSig = ExistingFunction->Signature; + const WasmSignature *OldSig = Existing->Signature; if (!OldSig) { - ExistingFunction->Signature = NewSig; - return; + Existing->Signature = NewSig; + return true; } - if (*NewSig != *OldSig) - warn("function signature mismatch: " + Existing->getName() + - "\n>>> defined as " + toString(*OldSig) + " in " + - toString(Existing->getFile()) + "\n>>> defined as " + - toString(*NewSig) + " in " + toString(File)); + return *NewSig == *OldSig; } static void checkGlobalType(const Symbol *Existing, const InputFile *File, @@ -255,26 +282,45 @@ Symbol *SymbolTable::addDefinedFunction(StringRef Name, uint32_t Flags, bool WasInserted; std::tie(S, WasInserted) = insert(Name, File); + auto Replace = [&](Symbol* Sym) { + // If the new defined function doesn't have signture (i.e. bitcode + // functions) but the old symbol does, then preserve the old signature + const WasmSignature *OldSig = S->getSignature(); + auto* NewSym = replaceSymbol<DefinedFunction>(Sym, Name, Flags, File, Function); + if (!NewSym->Signature) + NewSym->Signature = OldSig; + }; + if (WasInserted || S->isLazy()) { - replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function); + Replace(S); return S; } - if (Function) - checkFunctionType(S, File, &Function->Signature); + auto ExistingFunction = dyn_cast<FunctionSymbol>(S); + if (!ExistingFunction) { + reportTypeError(S, File, WASM_SYMBOL_TYPE_FUNCTION); + return S; + } - if (shouldReplace(S, File, Flags)) { - // If the new defined function doesn't have signture (i.e. bitcode - // functions) but the old symbol does then preserve the old signature - const WasmSignature *OldSig = nullptr; - if (auto* F = dyn_cast<FunctionSymbol>(S)) - OldSig = F->Signature; - if (auto *L = dyn_cast<LazySymbol>(S)) - OldSig = L->Signature; - auto NewSym = replaceSymbol<DefinedFunction>(S, Name, Flags, File, Function); - if (!NewSym->Signature) - NewSym->Signature = OldSig; + if (Function && !signatureMatches(ExistingFunction, &Function->Signature)) { + Symbol* Variant; + if (getFunctionVariant(S, &Function->Signature, File, &Variant)) + // New variant, always replace + Replace(Variant); + else if (shouldReplace(S, File, Flags)) + // Variant already exists, replace it after checking shouldReplace + Replace(Variant); + + // This variant we found take the place in the symbol table as the primary + // variant. + replace(Name, Variant); + return Variant; } + + // Existing function with matching signature. + if (shouldReplace(S, File, Flags)) + Replace(S); + return S; } @@ -287,15 +333,19 @@ Symbol *SymbolTable::addDefinedData(StringRef Name, uint32_t Flags, bool WasInserted; std::tie(S, WasInserted) = insert(Name, File); - if (WasInserted || S->isLazy()) { + auto Replace = [&]() { replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size); + }; + + if (WasInserted || S->isLazy()) { + Replace(); return S; } checkDataType(S, File); if (shouldReplace(S, File, Flags)) - replaceSymbol<DefinedData>(S, Name, Flags, File, Segment, Address, Size); + Replace(); return S; } @@ -307,15 +357,19 @@ Symbol *SymbolTable::addDefinedGlobal(StringRef Name, uint32_t Flags, bool WasInserted; std::tie(S, WasInserted) = insert(Name, File); - if (WasInserted || S->isLazy()) { + auto Replace = [&]() { replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global); + }; + + if (WasInserted || S->isLazy()) { + Replace(); return S; } checkGlobalType(S, File, &Global->getType()); if (shouldReplace(S, File, Flags)) - replaceSymbol<DefinedGlobal>(S, Name, Flags, File, Global); + Replace(); return S; } @@ -327,15 +381,19 @@ Symbol *SymbolTable::addDefinedEvent(StringRef Name, uint32_t Flags, bool WasInserted; std::tie(S, WasInserted) = insert(Name, File); - if (WasInserted || S->isLazy()) { + auto Replace = [&]() { replaceSymbol<DefinedEvent>(S, Name, Flags, File, Event); + }; + + if (WasInserted || S->isLazy()) { + Replace(); return S; } checkEventType(S, File, &Event->getType(), &Event->Signature); if (shouldReplace(S, File, Flags)) - replaceSymbol<DefinedEvent>(S, Name, Flags, File, Event); + Replace(); return S; } @@ -350,13 +408,25 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef Name, StringRef ImportName, bool WasInserted; std::tie(S, WasInserted) = insert(Name, File); - if (WasInserted) + auto Replace = [&]() { replaceSymbol<UndefinedFunction>(S, Name, ImportName, ImportModule, Flags, File, Sig); + }; + + if (WasInserted) + Replace(); else if (auto *Lazy = dyn_cast<LazySymbol>(S)) Lazy->fetch(); - else - checkFunctionType(S, File, Sig); + else { + auto ExistingFunction = dyn_cast<FunctionSymbol>(S); + if (!ExistingFunction) { + reportTypeError(S, File, WASM_SYMBOL_TYPE_FUNCTION); + return S; + } + if (!signatureMatches(ExistingFunction, Sig)) + if (getFunctionVariant(S, Sig, File, &S)) + Replace(); + } return S; } @@ -438,6 +508,44 @@ bool SymbolTable::addComdat(StringRef Name) { return Comdats.insert(CachedHashStringRef(Name)).second; } +// The new signature doesn't match. Create a variant to the symbol with the +// signature encoded in the name and return that instead. These symbols are +// then unified later in handleSymbolVariants. +bool SymbolTable::getFunctionVariant(Symbol* Sym, const WasmSignature *Sig, + const InputFile *File, Symbol **Out) { + StringRef NewName = getVariantName(Sym->getName(), *Sig); + LLVM_DEBUG(dbgs() << "getFunctionVariant: " << Sym->getName() << " -> " << NewName + << " " << toString(*Sig) << "\n"); + Symbol *Variant = nullptr; + + // Linear search through symbol variants. Should never be more than two + // or three entries here. + auto &Variants = SymVariants[CachedHashStringRef(Sym->getName())]; + if (Variants.size() == 0) + Variants.push_back(Sym); + + for (Symbol* V : Variants) { + if (*V->getSignature() == *Sig) { + Variant = V; + break; + } + } + + bool WasAdded = !Variant; + if (WasAdded) { + // Create a new variant; + LLVM_DEBUG(dbgs() << "added new variant\n"); + Variant = reinterpret_cast<Symbol *>(make<SymbolUnion>()); + Variants.push_back(Variant); + } else { + LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*Variant) << "\n"); + assert(*Variant->getSignature() == *Sig); + } + + *Out = Variant; + return WasAdded; +} + // Set a flag for --trace-symbol so that we can print out a log message // if a new symbol with the same name is inserted into the symbol table. void SymbolTable::trace(StringRef Name) { @@ -451,14 +559,16 @@ static const uint8_t UnreachableFn[] = { // Replace the given symbol body with an unreachable function. // This is used by handleWeakUndefines in order to generate a callable -// equivalent of an undefined function. +// equivalent of an undefined function and also handleSymbolVariants for +// undefined functions that don't match the signature of the definition. InputFunction *SymbolTable::replaceWithUnreachable(Symbol *Sym, const WasmSignature &Sig, StringRef DebugName) { auto *Func = make<SyntheticFunction>(Sig, Sym->getName(), DebugName); Func->setBody(UnreachableFn); SyntheticFunctions.emplace_back(Func); - replaceSymbol<DefinedFunction>(Sym, Sym->getName(), Sym->getFlags(), nullptr, Func); + replaceSymbol<DefinedFunction>(Sym, Sym->getName(), Sym->getFlags(), nullptr, + Func); return Func; } @@ -471,21 +581,15 @@ void SymbolTable::handleWeakUndefines() { if (!Sym->isUndefWeak()) continue; - const WasmSignature *Sig = nullptr; - - if (auto *FuncSym = dyn_cast<FunctionSymbol>(Sym)) { + const WasmSignature *Sig = Sym->getSignature(); + if (!Sig) { // It is possible for undefined functions not to have a signature (eg. if // added via "--undefined"), but weak undefined ones do have a signature. - assert(FuncSym->Signature); - Sig = FuncSym->Signature; - } else if (auto *LazySym = dyn_cast<LazySymbol>(Sym)) { - // Lazy symbols may not be functions and therefore can have a null - // signature. - Sig = LazySym->Signature; - } - - if (!Sig) + // Lazy symbols may not be functions and therefore Sig can still be null + // in some circumstantce. + assert(!isa<FunctionSymbol>(Sym)); continue; + } // Add a synthetic dummy for weak undefined functions. These dummies will // be GC'd if not used as the target of any "call" instructions. @@ -498,3 +602,64 @@ void SymbolTable::handleWeakUndefines() { Sym->setHidden(true); } } + +static void reportFunctionSignatureMismatch(StringRef SymName, + FunctionSymbol *A, + FunctionSymbol *B, bool Error) { + std::string msg = ("function signature mismatch: " + SymName + + "\n>>> defined as " + toString(*A->Signature) + " in " + + toString(A->getFile()) + "\n>>> defined as " + + toString(*B->Signature) + " in " + toString(B->getFile())) + .str(); + if (Error) + error(msg); + else + warn(msg); +} + +// Remove any variant symbols that were created due to function signature +// mismatches. +void SymbolTable::handleSymbolVariants() { + for (auto Pair : SymVariants) { + // Push the initial symbol onto the list of variants. + StringRef SymName = Pair.first.val(); + std::vector<Symbol *> &Variants = Pair.second; + +#ifndef NDEBUG + dbgs() << "symbol with (" << Variants.size() + << ") variants: " << SymName << "\n"; + for (auto *S: Variants) { + auto *F = cast<FunctionSymbol>(S); + dbgs() << " variant: " + F->getName() << " " << toString(*F->Signature) << "\n"; + } +#endif + + // Find the one definition. + DefinedFunction *Defined = nullptr; + for (auto *Symbol : Variants) { + if (auto F = dyn_cast<DefinedFunction>(Symbol)) { + Defined = F; + break; + } + } + + // If there are no definitions, and the undefined symbols disagree on + // the signature, there is not we can do since we don't know which one + // to use as the signature on the import. + if (!Defined) { + reportFunctionSignatureMismatch(SymName, + cast<FunctionSymbol>(Variants[0]), + cast<FunctionSymbol>(Variants[1]), true); + return; + } + + for (auto *Symbol : Variants) { + if (Symbol != Defined) { + auto *F = cast<FunctionSymbol>(Symbol); + reportFunctionSignatureMismatch(SymName, F, Defined, false); + StringRef DebugName = Saver.save("unreachable:" + toString(*F)); + replaceWithUnreachable(F, *F->Signature, DebugName); + } + } + } +} diff --git a/lld/wasm/SymbolTable.h b/lld/wasm/SymbolTable.h index f03980d7066..3d0529af41d 100644 --- a/lld/wasm/SymbolTable.h +++ b/lld/wasm/SymbolTable.h @@ -49,6 +49,8 @@ public: Symbol *find(StringRef Name); + void replace(StringRef Name, Symbol* Sym); + void trace(StringRef Name); Symbol *addDefinedFunction(StringRef Name, uint32_t Flags, InputFile *File, @@ -79,12 +81,15 @@ public: DefinedFunction *addSyntheticFunction(StringRef Name, uint32_t Flags, InputFunction *Function); + void handleSymbolVariants(); void handleWeakUndefines(); private: std::pair<Symbol *, bool> insert(StringRef Name, const InputFile *File); std::pair<Symbol *, bool> insertName(StringRef Name); + bool getFunctionVariant(Symbol* Sym, const WasmSignature *Sig, + const InputFile *File, Symbol **Out); InputFunction *replaceWithUnreachable(Symbol *Sym, const WasmSignature &Sig, StringRef DebugName); @@ -94,6 +99,10 @@ private: llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap; std::vector<Symbol *> SymVector; + // For certain symbols types, e.g. function symbols, we allow for muliple + // variants of the same symbol with different signatures. + llvm::DenseMap<llvm::CachedHashStringRef, std::vector<Symbol *>> SymVariants; + llvm::DenseSet<llvm::CachedHashStringRef> Comdats; // For LTO. diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index 60030bef3fa..f8ea47176a3 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -45,6 +45,14 @@ WasmSymbolType Symbol::getWasmType() const { llvm_unreachable("invalid symbol kind"); } +const WasmSignature *Symbol::getSignature() const { + if (auto* F = dyn_cast<FunctionSymbol>(this)) + return F->Signature; + if (auto *L = dyn_cast<LazySymbol>(this)) + return L->Signature; + return nullptr; +} + InputChunk *Symbol::getChunk() const { if (auto *F = dyn_cast<DefinedFunction>(this)) return F->Function; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index fd501ed64cd..3ed9e7abf55 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -105,6 +105,8 @@ public: // True if this symbol is specified by --trace-symbol option. unsigned Traced : 1; + const WasmSignature* getSignature() const; + protected: Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F) : IsUsedInRegularObj(false), ForceExport(false), Traced(false), |