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: 2380808080000B + 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: _start +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: Name: use_undef_global +; CHECK-NEXT: - Index: 3 +; CHECK-NEXT: Name: __wasm_call_ctors +; 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: _start +; NO-GC-NEXT: - Index: 3 +; NO-GC-NEXT: Name: use_undef_global +; NO-GC-NEXT: - Index: 4 +; NO-GC-NEXT: Name: __wasm_call_ctors +; 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" @@ -42,11 +43,11 @@ auto Enqueue = [&](Symbol *Sym) { if (!Sym) return; - InputChunk *Chunk = Sym->getChunk(); - if (!Chunk || Chunk->Live) + if (Sym->isLive()) return; - Chunk->Live = true; - Q.push_back(Chunk); + Sym->markLive(); + if (InputChunk *Chunk = Sym->getChunk()) + Q.push_back(Chunk); }; // Add GC root symbols. @@ -104,9 +105,15 @@ 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 (InputChunk *C : Symtab->SyntheticFunctions) if (!C->Live) message("removing unused section " + toString(C)); + 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,9 @@ // Indicates that this symbol will be included in the final image. bool isLive() const; + // Marks the symbol as Live, so that it will be included in the final image. + void markLive(); + void setHidden(bool IsHidden); uint32_t getOutputIndex() const; @@ -95,7 +99,8 @@ protected: Symbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F) - : Name(Name), SymbolKind(K), Flags(Flags), File(F) {} + : Name(Name), SymbolKind(K), Flags(Flags), File(F), + Live(!Config->GcSections) {} StringRef Name; Kind SymbolKind; @@ -103,6 +108,7 @@ InputFile *File; uint32_t OutputIndex = INVALID_INDEX; uint32_t OutputSymbolIndex = INVALID_INDEX; + bool Live; }; class FunctionSymbol : public Symbol { Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -74,8 +74,19 @@ return G->Global->Live; if (InputChunk *C = getChunk()) return C->Live; - // Assume any other kind of symbol is live. - return true; + return Live; +} + +void Symbol::markLive() { + if (auto *G = dyn_cast(this)) { + G->Global->Live = true; + return; + } + if (InputChunk *C = getChunk()) { + C->Live = true; + return; + } + Live = true; } uint32_t Symbol::getOutputSymbolIndex() const { Index: wasm/Writer.cpp =================================================================== --- wasm/Writer.cpp +++ wasm/Writer.cpp @@ -655,6 +655,8 @@ continue; if (Sym->isWeak() && !Config->Relocatable) continue; + if (!Sym->isLive()) + continue; DEBUG(dbgs() << "import: " << Sym->getName() << "\n"); Sym->setOutputIndex(ImportedSymbols.size()); @@ -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) {