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,65 @@ +; 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 +; (Should be "3" when function pruning implemented:) +; CHECK-NEXT: Index: 4 +; 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: 4180888080000B +; 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 @@ -161,6 +161,9 @@ } } + for (const auto &C : WasmObj->comdats()) + Symtab->addComdat(C.first(), this); + FunctionSymbols.resize(FunctionImports + WasmObj->functions().size()); GlobalSymbols.resize(GlobalImports + WasmObj->globals().size()); Index: wasm/InputSegment.cpp =================================================================== --- wasm/InputSegment.cpp +++ wasm/InputSegment.cpp @@ -18,6 +18,7 @@ uint32_t InputSegment::translateVA(uint32_t Address) const { assert(Address >= startVA() && Address < endVA()); + assert(OutputSeg); int32_t Delta = OutputSeg->StartVA + OutputSegmentOffset - startVA(); DEBUG(dbgs() << "translateVA: " << getName() << " Delta=" << Delta << " Address=" << Address << "\n"); Index: wasm/SymbolTable.h =================================================================== --- wasm/SymbolTable.h +++ wasm/SymbolTable.h @@ -57,11 +57,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 @@ -10,6 +10,7 @@ #include "SymbolTable.h" #include "Config.h" +#include "InputSegment.h" #include "WriterUtils.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" @@ -94,6 +95,16 @@ return &WasmObj->types()[FunctionType]; } +// Get the COMDAT for a given function symbol +static StringRef getFunctionComdat(const ObjFile &Obj, + const WasmSymbol &Sym) { + if (!Obj.isImportedFunction(Sym.ElementIndex)) { + uint32_t FunctionIndex = Sym.ElementIndex - Obj.NumFunctionImports(); + return Obj.getWasmObj()->functions()[FunctionIndex].Comdat; + } + return StringRef(); +} + // 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 checkSymbolTypes(const Symbol &Existing, const InputFile &F, @@ -154,10 +165,29 @@ bool WasInserted; Symbol::Kind Kind = Symbol::DefinedFunctionKind; const WasmSignature *NewSig = nullptr; - if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT) + StringRef Comdat; + if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT) { Kind = Symbol::DefinedGlobalKind; - else - NewSig = getFunctionSig(*cast(F), *Sym); + Comdat = Segment ? Segment->Segment->Data.Comdat : StringRef(); + } else { + ObjFile &OF = *cast(F); + NewSig = getFunctionSig(OF, *Sym); + Comdat = getFunctionComdat(OF, *Sym); + } + + if (!Comdat.empty() && ComdatMap[CachedHashStringRef(Comdat)] != F) { + DEBUG(dbgs() << "symbol " << Sym->Name << " of kind " << Kind + << " from COMDAT " << Comdat << " already defined\n"); + S = find(Sym->Name); + // Each object file that defines a COMDAT should define the same symbols in + // the COMDAT, since discarding COMDATs assumes that they are equivalent. + if (!S || !S->isDefined()) { + error("previous definition of COMDAT did not define symbol"); + return S; + } + checkSymbolTypes(*S, *F, *Sym, NewSig); + return S; + } std::tie(S, WasInserted) = insert(Sym->Name); if (WasInserted) { @@ -243,3 +273,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 @@ -644,6 +644,12 @@ S = make(Name); Segments.push_back(S); } + StringRef Comdat = Segment->Segment->Data.Comdat; + if (!Segment->Segment->Data.Comdat.empty() && + Symtab->findComdat(Comdat) != File) { + DEBUG(dbgs() << "discarding segment: " << Name << "\n"); + continue; + } S->addInputSegment(Segment); DEBUG(dbgs() << "added data: " << Name << ": " << S->Size << "\n"); }