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: test/wasm/relocatable.ll =================================================================== --- test/wasm/relocatable.ll +++ test/wasm/relocatable.ll @@ -17,6 +17,13 @@ @func_addr2 = hidden global i32()* @foo_import, align 4 @data_addr1 = hidden global i64* @data_import, align 8 +$func_comdat = comdat any +@data_comdat = weak_odr constant [3 x i8] c"abc", comdat($func_comdat) +define linkonce_odr i32 @func_comdat() comdat { +entry: + ret i32 ptrtoint ([3 x i8]* @data_comdat to i32) +} + ; CHECK: --- !WASM ; CHECK-NEXT: FileHeader: ; CHECK-NEXT: Version: 0x00000001 @@ -49,7 +56,7 @@ ; CHECK-NEXT: GlobalType: I32 ; CHECK-NEXT: GlobalMutable: false ; CHECK-NEXT: - Type: FUNCTION -; CHECK-NEXT: FunctionTypes: [ 0, 2 ] +; CHECK-NEXT: FunctionTypes: [ 0, 2, 2 ] ; CHECK-NEXT: - Type: TABLE ; CHECK-NEXT: Tables: ; CHECK-NEXT: - ElemType: ANYFUNC @@ -71,6 +78,11 @@ ; CHECK-NEXT: Mutable: false ; CHECK-NEXT: InitExpr: ; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 20 +; CHECK-NEXT: - Type: I32 +; CHECK-NEXT: Mutable: false +; CHECK-NEXT: InitExpr: +; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 8 ; CHECK-NEXT: - Type: I32 ; CHECK-NEXT: Mutable: false @@ -90,18 +102,24 @@ ; CHECK-NEXT: - Name: my_func ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 3 +; CHECK-NEXT: - Name: func_comdat +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: Index: 4 ; CHECK-NEXT: - Name: hello_str ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: Index: 1 -; CHECK-NEXT: - Name: func_addr1 +; CHECK-NEXT: - Name: data_comdat ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: Index: 2 -; CHECK-NEXT: - Name: func_addr2 +; CHECK-NEXT: - Name: func_addr1 ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: Index: 3 -; CHECK-NEXT: - Name: data_addr1 +; CHECK-NEXT: - Name: func_addr2 ; CHECK-NEXT: Kind: GLOBAL ; CHECK-NEXT: Index: 4 +; CHECK-NEXT: - Name: data_addr1 +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: Index: 5 ; CHECK-NEXT: - Type: ELEM ; CHECK-NEXT: Segments: ; CHECK-NEXT: - Offset: @@ -119,11 +137,16 @@ ; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB ; CHECK-NEXT: Index: 1 ; CHECK-NEXT: Offset: 0x00000013 +; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB +; CHECK-NEXT: Index: 2 +; CHECK-NEXT: Offset: 0x0000001F ; CHECK-NEXT: Functions: ; CHECK-NEXT: - Locals: -; CHECK-NEXT: Body: 4180808080001080808080000B +; CHECK-NEXT: Body: 4180808080001080808080000B ; CHECK-NEXT: - Locals: -; CHECK-NEXT: Body: 1081808080001A41010B +; CHECK-NEXT: Body: 1081808080001A41010B +; CHECK-NEXT: - Locals: +; CHECK-NEXT: Body: 4194808080000B ; CHECK-NEXT: - Type: DATA ; CHECK-NEXT: Relocations: ; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32 @@ -160,26 +183,43 @@ ; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Value: 16 ; CHECK-NEXT: Content: FFFFFFFF +; CHECK-NEXT: - SectionOffset: 45 +; CHECK-NEXT: MemoryIndex: 0 +; CHECK-NEXT: Offset: +; CHECK-NEXT: Opcode: I32_CONST +; CHECK-NEXT: Value: 20 +; CHECK-NEXT: Content: '616263' ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: linking -; CHECK-NEXT: DataSize: 20 +; CHECK-NEXT: DataSize: 23 ; CHECK-NEXT: SegmentInfo: ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Name: .rodata.hello_str ; CHECK-NEXT: Alignment: 1 -; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: Flags: [ ] ; CHECK-NEXT: - Index: 1 ; CHECK-NEXT: Name: .data.func_addr1 ; CHECK-NEXT: Alignment: 4 -; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: Flags: [ ] ; CHECK-NEXT: - Index: 2 ; CHECK-NEXT: Name: .data.func_addr2 ; CHECK-NEXT: Alignment: 4 -; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: Flags: [ ] ; CHECK-NEXT: - Index: 3 ; CHECK-NEXT: Name: .data.data_addr1 ; CHECK-NEXT: Alignment: 8 -; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: - Index: 4 +; CHECK-NEXT: Name: .rodata.data_comdat +; CHECK-NEXT: Alignment: 1 +; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: Comdats: +; CHECK-NEXT: - Name: func_comdat +; CHECK-NEXT: Entries: +; CHECK-NEXT: - Kind: FUNCTION +; CHECK-NEXT: Index: 4 +; CHECK-NEXT: - Kind: DATA +; CHECK-NEXT: Index: 4 ; CHECK-NEXT: - Type: CUSTOM ; CHECK-NEXT: Name: name ; CHECK-NEXT: FunctionNames: Index: wasm/InputChunks.h =================================================================== --- wasm/InputChunks.h +++ wasm/InputChunks.h @@ -42,6 +42,7 @@ virtual const uint8_t *getData() const = 0; virtual uint32_t getSize() const = 0; virtual uint32_t getInputSectionOffset() const = 0; + virtual StringRef getComdat() const = 0; int32_t OutputOffset = 0; int32_t InputOffset = 0; @@ -82,6 +83,8 @@ uint32_t getInputSectionOffset() const override { return Segment.SectionOffset; } + StringRef getComdat() const override { return Segment.Data.Comdat; } + uint32_t getAlignment() const { return Segment.Data.Alignment; } uint32_t startVA() const { return Segment.Data.Offset.Value.Int32; } uint32_t endVA() const { return startVA() + getSize(); } @@ -111,6 +114,7 @@ uint32_t getInputSectionOffset() const override { return Function.CodeSectionOffset; }; + StringRef getComdat() const override { return Function.Comdat; } uint32_t getOutputIndex() const { return OutputIndex.getValue(); }; bool hasOutputIndex() const { return OutputIndex.hasValue(); }; Index: wasm/InputFiles.cpp =================================================================== --- wasm/InputFiles.cpp +++ wasm/InputFiles.cpp @@ -172,6 +172,9 @@ ArrayRef Types = WasmObj->types(); ArrayRef Globals = WasmObj->globals(); + for (const auto &C : WasmObj->comdats()) + Symtab->addComdat(C, this); + FunctionSymbols.resize(NumFunctionImports + Funcs.size()); GlobalSymbols.resize(NumGlobalImports + Globals.size()); @@ -236,7 +239,9 @@ // 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]->getComdat(); + ObjFile *ComdatFile = Comdat.empty() ? this : Symtab->findComdat(Comdat); + if (!ReachedFunctions[I] || ComdatFile != this) Functions[I]->Discarded = true; } for (uint32_t I = 0; I < Globals.size(); ++I) { @@ -249,6 +254,11 @@ if (!ReachedGlobals[I] && Seg->isCoveredByGlobal(Globals[I])) Seg->Discarded = true; } + for (InputSegment *Seg : Segments) { + StringRef Comdat = Seg->getComdat(); + if (!Comdat.empty() && Symtab->findComdat(Comdat) != this) + Seg->Discarded = true; + } // Populate `TableSymbols` with all symbols that are called indirectly uint32_t SegmentCount = WasmObj->elements().size(); 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 @@ -24,6 +24,7 @@ #include "llvm/Support/LEB128.h" #include +#include #define DEBUG_TYPE "lld" @@ -430,6 +431,42 @@ SubSection.finalizeContents(); SubSection.writeToStream(OS); } + + struct ComdatEntry { unsigned Kind; uint32_t Index; }; + std::map> Comdats; + uint32_t NumFunctionImports = ImportedFunctions.size(); + + for (uint32_t I = 0; I < DefinedFunctions.size(); ++I) { + StringRef Comdat = DefinedFunctions[I]->getComdat(); + if (!Comdat.empty()) + Comdats[Comdat].emplace_back( + ComdatEntry{WASM_COMDAT_FUNCTION, I + NumFunctionImports}); + } + for (uint32_t I = 0; I < Segments.size(); ++I) { + const auto &InputSegments = Segments[I]->InputSegments; + StringRef Comdat = InputSegments.empty() ? StringRef() + : InputSegments[0]->getComdat(); + for (const InputSegment *IS : InputSegments) + assert(IS->getComdat() == Comdat); + if (!Comdat.empty()) + Comdats[Comdat].emplace_back(ComdatEntry{WASM_COMDAT_DATA, I}); + } + + if (!Comdats.empty()) { + SubSection SubSection(WASM_COMDAT_INFO); + writeUleb128(SubSection.getStream(), Comdats.size(), "num comdats"); + for (const auto &C : Comdats) { + writeStr(SubSection.getStream(), C.first, "comdat name"); + writeUleb128(SubSection.getStream(), 0, "comdat flags"); // flags for future use + writeUleb128(SubSection.getStream(), C.second.size(), "num entries"); + for (const ComdatEntry &Entry : C.second) { + writeUleb128(SubSection.getStream(), Entry.Kind, "entry kind"); + writeUleb128(SubSection.getStream(), Entry.Index, "entry index"); + } + } + SubSection.finalizeContents(); + SubSection.writeToStream(OS); + } } // Create the custom "name" section containing debug symbol names.