Index: test/wasm/Inputs/undefined-globals.yaml =================================================================== --- /dev/null +++ test/wasm/Inputs/undefined-globals.yaml @@ -0,0 +1,51 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ReturnType: I64 + ParamTypes: + - Type: IMPORT + Imports: + - Module: env + Field: unused_undef_global + Kind: GLOBAL + GlobalType: I64 + GlobalMutable: true + - Module: env + Field: used_undef_global + Kind: GLOBAL + GlobalType: I64 + GlobalMutable: true + - Type: FUNCTION + FunctionTypes: [ 0 ] + - Type: CODE + Functions: + - Index: 0 + Locals: + Body: 2381808080000B + Relocations: + - Type: R_WEBASSEMBLY_GLOBAL_INDEX_LEB + Index: 1 + Offset: 0x00000004 + - Type: CUSTOM + Name: linking + SymbolTable: + - Index: 0 + Kind: GLOBAL + Name: unused_undef_global + Flags: [ VISIBILITY_HIDDEN, UNDEFINED ] + Global: 0 + - Index: 1 + Kind: GLOBAL + Name: used_undef_global + Flags: [ VISIBILITY_HIDDEN, UNDEFINED ] + Global: 1 + - Index: 2 + Kind: FUNCTION + Name: use_undef_global + Flags: [ VISIBILITY_HIDDEN ] + Function: 0 +... Index: test/wasm/gc-imports.ll =================================================================== --- /dev/null +++ test/wasm/gc-imports.ll @@ -0,0 +1,111 @@ +; RUN: llc -filetype=obj %s -o %t.o +; RUN: yaml2obj %S/Inputs/undefined-globals.yaml -o %t_globals.o +; RUN: wasm-ld -print-gc-sections --allow-undefined -o %t1.wasm %t.o %t_globals.o + +target triple = "wasm32-unknown-unknown-wasm" + +declare hidden i64 @unused_undef_function(i64 %arg) + +declare hidden i32 @used_undef_function() + +declare i64 @use_undef_global() + +define hidden void @_start() { +entry: + call i32 @used_undef_function() + call i64 @use_undef_global() + ret void +} + +; RUN: obj2yaml %t1.wasm | FileCheck %s + +; CHECK: - Type: TYPE +; CHECK-NEXT: Signatures: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: ReturnType: I32 +; CHECK-NEXT: ParamTypes: +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: ReturnType: NORESULT +; CHECK-NEXT: ParamTypes: +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: ReturnType: I64 +; CHECK-NEXT: ParamTypes: +; CHECK-NEXT: - Type: IMPORT +; CHECK-NEXT: Imports: +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: used_undef_function +; CHECK-NEXT: Kind: FUNCTION +; CHECK-NEXT: SigIndex: 0 +; CHECK-NEXT: - Module: env +; CHECK-NEXT: Field: used_undef_global +; CHECK-NEXT: Kind: GLOBAL +; CHECK-NEXT: GlobalType: I64 +; CHECK-NEXT: GlobalMutable: true +; CHECK-NEXT: - Type: +; CHECK: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: used_undef_function +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: __wasm_call_ctors +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: Name: _start +; CHECK-NEXT: - Index: 3 +; CHECK-NEXT: Name: use_undef_global +; CHECK-NEXT: ... + +; RUN: wasm-ld -print-gc-sections --no-gc-sections --allow-undefined \ +; RUN: -o %t1.no-gc.wasm %t.o %t_globals.o +; RUN: obj2yaml %t1.no-gc.wasm | FileCheck %s -check-prefix=NO-GC + +; NO-GC: - Type: TYPE +; NO-GC-NEXT: Signatures: +; NO-GC-NEXT: - Index: 0 +; NO-GC-NEXT: ReturnType: I32 +; NO-GC-NEXT: ParamTypes: +; NO-GC-NEXT: - Index: 1 +; NO-GC-NEXT: ReturnType: I64 +; NO-GC-NEXT: ParamTypes: +; NO-GC-NEXT: - I64 +; NO-GC-NEXT: - Index: 2 +; NO-GC-NEXT: ReturnType: NORESULT +; NO-GC-NEXT: ParamTypes: +; NO-GC-NEXT: - Index: 3 +; NO-GC-NEXT: ReturnType: I64 +; NO-GC-NEXT: ParamTypes: +; NO-GC-NEXT: - Type: IMPORT +; NO-GC-NEXT: Imports: +; NO-GC-NEXT: - Module: env +; NO-GC-NEXT: Field: used_undef_function +; NO-GC-NEXT: Kind: FUNCTION +; NO-GC-NEXT: SigIndex: 0 +; NO-GC-NEXT: - Module: env +; NO-GC-NEXT: Field: unused_undef_function +; NO-GC-NEXT: Kind: FUNCTION +; NO-GC-NEXT: SigIndex: 1 +; NO-GC-NEXT: - Module: env +; NO-GC-NEXT: Field: unused_undef_global +; NO-GC-NEXT: Kind: GLOBAL +; NO-GC-NEXT: GlobalType: I64 +; NO-GC-NEXT: GlobalMutable: true +; NO-GC-NEXT: - Module: env +; NO-GC-NEXT: Field: used_undef_global +; NO-GC-NEXT: Kind: GLOBAL +; NO-GC-NEXT: GlobalType: I64 +; NO-GC-NEXT: GlobalMutable: true +; NO-GC-NEXT: - Type: +; NO-GC: - Type: CUSTOM +; NO-GC-NEXT: Name: name +; NO-GC-NEXT: FunctionNames: +; NO-GC-NEXT: - Index: 0 +; NO-GC-NEXT: Name: used_undef_function +; NO-GC-NEXT: - Index: 1 +; NO-GC-NEXT: Name: unused_undef_function +; NO-GC-NEXT: - Index: 2 +; NO-GC-NEXT: Name: __wasm_call_ctors +; NO-GC-NEXT: - Index: 3 +; NO-GC-NEXT: Name: _start +; NO-GC-NEXT: - Index: 4 +; NO-GC-NEXT: Name: use_undef_global +; NO-GC-NEXT: ... Index: wasm/MarkLive.cpp =================================================================== --- wasm/MarkLive.cpp +++ wasm/MarkLive.cpp @@ -22,6 +22,7 @@ #include "MarkLive.h" #include "Config.h" #include "InputChunks.h" +#include "InputGlobal.h" #include "SymbolTable.h" #include "Symbols.h" @@ -40,13 +41,11 @@ SmallVector Q; auto Enqueue = [&](Symbol *Sym) { - if (!Sym) + if (!Sym || Sym->isLive()) return; - InputChunk *Chunk = Sym->getChunk(); - if (!Chunk || Chunk->Live) - return; - Chunk->Live = true; - Q.push_back(Chunk); + Sym->markLive(); + if (InputChunk *Chunk = Sym->getChunk()) + Q.push_back(Chunk); }; // Add GC root symbols. @@ -104,6 +103,12 @@ for (InputChunk *C : Obj->Segments) if (!C->Live) message("removing unused section " + toString(C)); + for (InputGlobal *G : Obj->Globals) + if (!G->Live) + message("removing unused section " + toString(G)); } + for (InputGlobal *G : Symtab->SyntheticGlobals) + if (!G->Live) + message("removing unused section " + toString(G)); } } Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -10,6 +10,7 @@ #ifndef LLD_WASM_SYMBOLS_H #define LLD_WASM_SYMBOLS_H +#include "Config.h" #include "lld/Common/LLVM.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Wasm.h" @@ -74,6 +75,10 @@ // Indicates that this symbol will be included in the final image. bool isLive() const; + // Marks the symbol's InputChunk or import as Live, so that it will be + // included in the final image. + void markLive(); + void setHidden(bool IsHidden); // Get/set the index in the output symbol table. This is only used for @@ -140,11 +145,14 @@ public: UndefinedFunction(StringRef Name, uint32_t Flags, InputFile *File = nullptr, const WasmSignature *Type = nullptr) - : FunctionSymbol(Name, UndefinedFunctionKind, Flags, File, Type) {} + : FunctionSymbol(Name, UndefinedFunctionKind, Flags, File, Type), + ImportLive(!Config->GcSections) {} static bool classof(const Symbol *S) { return S->kind() == UndefinedFunctionKind; } + + bool ImportLive; }; class DataSymbol : public Symbol { @@ -237,11 +245,14 @@ public: UndefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File = nullptr, const WasmGlobalType *Type = nullptr) - : GlobalSymbol(Name, UndefinedGlobalKind, Flags, File, Type) {} + : GlobalSymbol(Name, UndefinedGlobalKind, Flags, File, Type), + ImportLive(!Config->GcSections) {} static bool classof(const Symbol *S) { return S->kind() == UndefinedGlobalKind; } + + bool ImportLive; }; class LazySymbol : public Symbol { Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -48,14 +48,53 @@ } bool Symbol::isLive() const { - if (auto *G = dyn_cast(this)) - return G->Global->Live; - if (InputChunk *C = getChunk()) - return C->Live; - // Assume any other kind of symbol is live. + switch (SymbolKind) { + case DefinedFunctionKind: + return cast(this)->Function->Live; + case DefinedDataKind: + if (InputSegment *S = cast(this)->Segment) + return S->Live; + break; + case DefinedGlobalKind: + return cast(this)->Global->Live; + case UndefinedFunctionKind: + return cast(this)->ImportLive; + case UndefinedGlobalKind: + return cast(this)->ImportLive; + default: + break; + } + // Assume any other kind of symbol is live (we don't track liveness for + // undefined data, or for synthetic data symbols with no InputSegment). return true; } +void Symbol::markLive() { + switch (SymbolKind) { + case DefinedFunctionKind: + cast(this)->Function->Live = true; + break; + case DefinedDataKind: + if (InputSegment *S = cast(this)->Segment) + S->Live = true; + break; + case DefinedGlobalKind: + cast(this)->Global->Live = true; + break; + case UndefinedFunctionKind: + cast(this)->ImportLive = true; + break; + case UndefinedGlobalKind: + cast(this)->ImportLive = true; + break; + case UndefinedDataKind: + // No import for undefined data - nothing to mark live + break; + default: + llvm_unreachable("cannot mark symbol live"); + } +} + uint32_t Symbol::getOutputSymbolIndex() const { assert(OutputSymbolIndex != INVALID_INDEX); return OutputSymbolIndex; Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -658,6 +658,8 @@ continue; if (Sym->isWeak() && !Config->Relocatable) continue; + if (!Sym->isLive()) + continue; DEBUG(dbgs() << "import: " << Sym->getName() << "\n"); ImportedSymbols.emplace_back(Sym); @@ -786,12 +788,6 @@ // Mark target type as live File->TypeMap[Reloc.Index] = registerType(Types[Reloc.Index]); File->TypeIsUsed[Reloc.Index] = true; - } else if (Reloc.Type == R_WEBASSEMBLY_GLOBAL_INDEX_LEB && - Config->GcSections) { - // Mark target global as live - GlobalSymbol *Sym = File->getGlobalSymbol(Reloc.Index); - if (auto *G = dyn_cast(Sym)) - G->Global->Live = true; } } }; @@ -804,17 +800,6 @@ HandleRelocs(Chunk); } - if (Config->PrintGcSections) { - for (const ObjFile *Obj : Symtab->ObjectFiles) { - for (InputGlobal *G : Obj->Globals) - if (!G->Live) - message("removing unused section " + toString(G)); - } - for (InputGlobal *G : Symtab->SyntheticGlobals) - if (!G->Live) - message("removing unused section " + toString(G)); - } - uint32_t GlobalIndex = NumImportedGlobals + InputGlobals.size(); auto AddDefinedGlobal = [&](InputGlobal *Global) { if (Global->Live) {