diff --git a/lld/test/wasm/Inputs/comdat1.ll b/lld/test/wasm/Inputs/comdat1.ll --- a/lld/test/wasm/Inputs/comdat1.ll +++ b/lld/test/wasm/Inputs/comdat1.ll @@ -1,13 +1,22 @@ target triple = "wasm32-unknown-unknown" -$inlineFn = comdat any -@constantData = weak_odr constant [3 x i8] c"abc", comdat($inlineFn) -define linkonce_odr i32 @inlineFn() comdat { -entry: +$foo = comdat any + +@constantData = constant [3 x i8] c"abc", comdat($foo) + +define i32 @comdatFn() comdat($foo) { ret i32 ptrtoint ([3 x i8]* @constantData to i32) } -define i32 @callInline1() { -entry: - ret i32 ptrtoint (i32 ()* @inlineFn to i32) +define internal void @do_init() comdat($foo) { + ret void +} + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void +()*, i8* } { i32 65535, void ()* @do_init, i8* null }] + +; Everything above this is part ofthe `foo` comdat group + +define i32 @callComdatFn1() { + ret i32 ptrtoint (i32 ()* @comdatFn to i32) } diff --git a/lld/test/wasm/Inputs/comdat2.ll b/lld/test/wasm/Inputs/comdat2.ll --- a/lld/test/wasm/Inputs/comdat2.ll +++ b/lld/test/wasm/Inputs/comdat2.ll @@ -1,13 +1,23 @@ target triple = "wasm32-unknown-unknown" -$inlineFn = comdat any -@constantData = weak_odr constant [3 x i8] c"abc", comdat($inlineFn) -define linkonce_odr i32 @inlineFn() comdat { -entry: +$foo = comdat any + +@constantData = constant [3 x i8] c"abc", comdat($foo) + +define i32 @comdatFn() comdat($foo) { ret i32 ptrtoint ([3 x i8]* @constantData to i32) } -define i32 @callInline2() { -entry: - ret i32 ptrtoint (i32 ()* @inlineFn to i32) +define internal void @do_init() comdat($foo) { + ret void +} + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void +()*, i8* } { i32 65535, void ()* @do_init, i8* null }] + + +; Everything above this is part ofthe `foo` comdat group + +define i32 @callComdatFn2() { + ret i32 ptrtoint (i32 ()* @comdatFn to i32) } diff --git a/lld/test/wasm/comdats.ll b/lld/test/wasm/comdats.ll --- a/lld/test/wasm/comdats.ll +++ b/lld/test/wasm/comdats.ll @@ -6,11 +6,11 @@ target triple = "wasm32-unknown-unknown" -declare i32 @inlineFn() +declare i32 @comdatFn() -define void @_start() local_unnamed_addr { +define void @_start() { entry: - %call = call i32 @inlineFn() + %call = call i32 @comdatFn() ret void } @@ -36,16 +36,16 @@ ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 -; CHECK-NEXT: - Name: inlineFn +; CHECK-NEXT: - Name: comdatFn ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 1 ; CHECK-NEXT: - Name: constantData ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: - Name: callInline1 +; CHECK-NEXT: - Name: callComdatFn1 ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 2 -; CHECK-NEXT: - Name: callInline2 +; CHECK-NEXT: - Name: callComdatFn2 ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 3 ; CHECK-NEXT: - Type: ELEM diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h --- a/lld/wasm/InputChunks.h +++ b/lld/wasm/InputChunks.h @@ -64,9 +64,12 @@ // If GC is disabled, all sections start out as live by default. unsigned Live : 1; + // Signals the check was discarded by COMDAT handling. + unsigned Discarded : 1; + protected: InputChunk(ObjFile *F, Kind K) - : File(F), Live(!Config->GcSections), SectionKind(K) {} + : File(F), Live(!Config->GcSections), Discarded(false), SectionKind(K) {} virtual ~InputChunk() = default; virtual ArrayRef data() const = 0; diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -306,14 +306,17 @@ TypeIsUsed.resize(getWasmObj()->types().size(), false); ArrayRef Comdats = WasmObj->linkingData().Comdats; - for (unsigned I = 0; I < Comdats.size(); ++I) { - bool IsNew = IgnoreComdats || Symtab->addComdat(Comdats[I]); + for (StringRef Comdat : Comdats) { + bool IsNew = IgnoreComdats || Symtab->addComdat(Comdat); KeptComdats.push_back(IsNew); } // Populate `Segments`. - for (const WasmSegment &S : WasmObj->dataSegments()) - Segments.emplace_back(make(S, this)); + for (const WasmSegment &S : WasmObj->dataSegments()) { + auto* Seg = make(S, this); + Seg->Discarded = isExcludedByComdat(Seg); + Segments.emplace_back(Seg); + } setRelocs(Segments, DataSection); // Populate `Functions`. @@ -322,9 +325,11 @@ ArrayRef Types = WasmObj->types(); Functions.reserve(Funcs.size()); - for (size_t I = 0, E = Funcs.size(); I != E; ++I) - Functions.emplace_back( - make(Types[FuncTypes[I]], &Funcs[I], this)); + for (size_t I = 0, E = Funcs.size(); I != E; ++I) { + auto* Func = make(Types[FuncTypes[I]], &Funcs[I], this); + Func->Discarded = isExcludedByComdat(Func); + Functions.emplace_back(Func); + } setRelocs(Functions, CodeSection); // Populate `Globals`. @@ -387,21 +392,16 @@ case WASM_SYMBOL_TYPE_FUNCTION: { InputFunction *Func = Functions[Sym.Info.ElementIndex - WasmObj->getNumImportedFunctions()]; - if (isExcludedByComdat(Func)) { - Func->Live = false; + if (Func->Discarded) return nullptr; - } - if (Sym.isBindingLocal()) return make(Name, Flags, this, Func); return Symtab->addDefinedFunction(Name, Flags, this, Func); } case WASM_SYMBOL_TYPE_DATA: { InputSegment *Seg = Segments[Sym.Info.DataRef.Segment]; - if (isExcludedByComdat(Seg)) { - Seg->Live = false; + if (Seg->Discarded) return nullptr; - } uint32_t Offset = Sym.Info.DataRef.Offset; uint32_t Size = Sym.Info.DataRef.Size; @@ -439,12 +439,22 @@ switch (Sym.Info.Kind) { case WASM_SYMBOL_TYPE_FUNCTION: + if (Sym.isBindingLocal()) + return make(Name, Sym.Info.ImportName, + Sym.Info.ImportModule, Flags, this, + Sym.Signature, IsCalledDirectly); return Symtab->addUndefinedFunction(Name, Sym.Info.ImportName, Sym.Info.ImportModule, Flags, this, Sym.Signature, IsCalledDirectly); case WASM_SYMBOL_TYPE_DATA: + if (Sym.isBindingLocal()) + return make(Name, Flags, this); return Symtab->addUndefinedData(Name, Flags, this); case WASM_SYMBOL_TYPE_GLOBAL: + if (Sym.isBindingLocal()) + return make(Name, Sym.Info.ImportName, + Sym.Info.ImportModule, Flags, this, + Sym.GlobalType); return Symtab->addUndefinedGlobal(Name, Sym.Info.ImportName, Sym.Info.ImportModule, Flags, this, Sym.GlobalType); diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -52,8 +52,11 @@ if (Sym == WasmSym::CallCtors) { for (const ObjFile *Obj : Symtab->ObjectFiles) { const WasmLinkingData &L = Obj->getWasmObj()->linkingData(); - for (const WasmInitFunc &F : L.InitFunctions) - Enqueue(Obj->getFunctionSymbol(F.Symbol)); + for (const WasmInitFunc &F : L.InitFunctions) { + auto* InitSym = Obj->getFunctionSymbol(F.Symbol); + if (!InitSym->isDiscarded()) + Enqueue(InitSym); + } } } }; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -69,6 +69,9 @@ bool isWeak() const; bool isHidden() const; + // Returns true if this symbol exists in a discarded (due to COMDAT) section + bool isDiscarded() const; + // True if this is an undefined weak symbol. This only works once // all input files have been added. bool isUndefWeak() const { diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -63,6 +63,12 @@ return nullptr; } +bool Symbol::isDiscarded() const { + if (InputChunk *C = getChunk()) + return C->Discarded; + return false; +} + bool Symbol::isLive() const { if (auto *G = dyn_cast(this)) return G->Global->Live; @@ -74,6 +80,7 @@ } void Symbol::markLive() { + assert(!isDiscarded()); if (auto *G = dyn_cast(this)) G->Global->Live = true; if (auto *E = dyn_cast(this)) diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -701,6 +701,9 @@ const WasmLinkingData &L = File->getWasmObj()->linkingData(); for (const WasmInitFunc &F : L.InitFunctions) { FunctionSymbol *Sym = File->getFunctionSymbol(F.Symbol); + // comdat exclusions can cause init functions be discarded. + if (Sym->isDiscarded()) + continue; assert(Sym->isLive()); if (*Sym->Signature != WasmSignature{{}, {}}) error("invalid signature for init func: " + toString(*Sym));