diff options
Diffstat (limited to 'lld/wasm/SymbolTable.cpp')
-rw-r--r-- | lld/wasm/SymbolTable.cpp | 275 |
1 files changed, 220 insertions, 55 deletions
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); + } + } + } +} |