Index: test/wasm/Inputs/comdat1.ll =================================================================== --- /dev/null +++ test/wasm/Inputs/comdat1.ll @@ -0,0 +1,11 @@ +$inlineFn = comdat any +@constantData = weak_odr constant [3 x i8] c"abc", comdat($inlineFn) +define linkonce_odr i32 @inlineFn() comdat { +entry: + ret i32 ptrtoint ([3 x i8]* @constantData to i32) +} + +define i32 @callInline1() { +entry: + ret i32 ptrtoint (i32 ()* @inlineFn to i32) +} Index: test/wasm/Inputs/comdat2.ll =================================================================== --- /dev/null +++ test/wasm/Inputs/comdat2.ll @@ -0,0 +1,11 @@ +$inlineFn = comdat any +@constantData = weak_odr constant [3 x i8] c"abc", comdat($inlineFn) +define linkonce_odr i32 @inlineFn() comdat { +entry: + ret i32 ptrtoint ([3 x i8]* @constantData to i32) +} + +define i32 @callInline2() { +entry: + ret i32 ptrtoint (i32 ()* @inlineFn to i32) +} Index: test/wasm/comdats.ll =================================================================== --- /dev/null +++ test/wasm/comdats.ll @@ -0,0 +1,62 @@ +; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/comdat1.ll -o %t1.o +; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/comdat2.ll -o %t2.o +; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o +; RUN: lld -flavor wasm -o %t.wasm %t.o %t1.o %t2.o +; RUN: obj2yaml %t.wasm | FileCheck %s + +declare i32 @inlineFn() + +define void @_start() local_unnamed_addr { +entry: + %call = call i32 @inlineFn() + ret void +} + +; CHECK: - Type: GLOBAL +; CHECK-NEXT: Globals: +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: true +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 66576 +; CHECK-NEXT: - Type: EXPORT +; CHECK-NEXT: Exports: +; CHECK-NEXT: - Name: memory +; CHECK-NEXT: Kind: MEMORY +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: - Name: _start +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 0 +; CHECK-NEXT: - Name: inlineFn +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 1 +; CHECK-NEXT: - Name: callInline1 +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 2 +; CHECK-NEXT: - Name: callInline2 +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 3 +; CHECK-NEXT: - Type: ELEM +; CHECK-NEXT: Segments: +; CHECK-NEXT: - Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1 +; CHECK-NEXT: Functions: [ 1 ] +; CHECK-NEXT: - Type: CODE +; CHECK-NEXT: Functions: +; CHECK-NEXT: - Locals: +; CHECK-NEXT: Body: 1081808080001A0B +; CHECK-NEXT: - Locals: +; CHECK-NEXT: Body: 4180888080000B +; CHECK-NEXT: - Locals: +; CHECK-NEXT: Body: 4181808080000B +; CHECK-NEXT: - Locals: +; CHECK-NEXT: Body: 4181808080000B +; CHECK-NEXT: - Type: DATA +; CHECK-NEXT: Segments: +; CHECK-NEXT: - SectionOffset: 7 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 1024 +; CHECK-NEXT: Content: '616263' Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -141,8 +141,7 @@ return Segment; } } - error("symbol not found in any segment: " + WasmSym.Name); - return nullptr; + fatal("symbol not found in any segment: " + WasmSym.Name); } static void copyRelocationsRange(std::vector &To, @@ -188,6 +187,9 @@ } } + for (const auto &C : WasmObj->comdats()) + Symtab->addComdat(C.first(), this); + FunctionSymbols.resize(NumFunctionImports + WasmObj->functions().size()); GlobalSymbols.resize(NumGlobalImports + WasmObj->globals().size()); @@ -254,10 +256,20 @@ // the weak symbol), then it's discardable. We aren't considering here // whether the Symbol is actually ever called. for (uint32_t I = 0; I < Funcs.size(); ++I) { - if (!ReachedFunctions[I]) + StringRef Comdat = Functions[I]->Function->Comdat; + ObjFile *ComdatFile = Comdat.empty() ? this : Symtab->findComdat(Comdat); + if (!ReachedFunctions[I] || ComdatFile != this) Functions[I] = &InputFunction::Discarded; } + for (InputSegment *&Seg : Segments) { + StringRef Comdat = Seg->Segment->Data.Comdat; + if (!Comdat.empty() && Symtab->findComdat(Comdat) != this) { + DEBUG(dbgs() << "discarding segment: " << Seg->getName() << "\n"); + Seg = &InputSegment::Discarded; + } + } + // Populate `TableSymbols` with all symbols that are called indirectly uint32_t SegmentCount = WasmObj->elements().size(); if (SegmentCount) { Index: wasm/SymbolTable.h =================================================================== --- wasm/SymbolTable.h +++ wasm/SymbolTable.h @@ -58,11 +58,15 @@ Symbol *addDefinedGlobal(StringRef Name); void addLazy(ArchiveFile *F, const Archive::Symbol *Sym); + bool addComdat(StringRef Name, ObjFile *); + ObjFile *findComdat(StringRef Name) const; + private: std::pair insert(StringRef Name); llvm::DenseMap SymMap; std::vector SymVector; + llvm::DenseMap ComdatMap; }; extern SymbolTable *Symtab; Index: wasm/SymbolTable.cpp =================================================================== --- wasm/SymbolTable.cpp +++ wasm/SymbolTable.cpp @@ -225,3 +225,19 @@ F->addMember(Sym); } } + +bool SymbolTable::addComdat(StringRef Name, ObjFile *F) { + DEBUG(dbgs() << "addComdat: " << Name << "\n"); + ObjFile *&File = ComdatMap[CachedHashStringRef(Name)]; + if (File) { + DEBUG(dbgs() << "COMDAT already defined\n"); + return false; + } + File = F; + return true; +} + +ObjFile *SymbolTable::findComdat(StringRef Name) const { + auto It = ComdatMap.find(CachedHashStringRef(Name)); + return It == ComdatMap.end() ? nullptr : It->second; +} Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -408,6 +408,10 @@ SubSection.finalizeContents(); SubSection.writeToStream(OS); } + + // TODO How important is relocatable output - do we need to output the Comdats? + // In that case we may need to fiddling with how we merge InputSegments into + // OutputSegments so that we can write out the Comdats here. } // Create the custom "name" section containing debug symbol names.