From c69119a92799a8dd09b7e47c3be52e13b81a2cdc Mon Sep 17 00:00:00 2001 From: Sam Clegg <sbc@chromium.org> Date: Wed, 31 Jan 2018 01:45:47 +0000 Subject: [PATCH] [WebAssembly] Add support for --gc-sections In this initial version we only GC symbols with `hidden` visibility since other symbols we export to the embedder. We could potentially modify this the future and only use symbols explicitly passed via `--export` as GC roots. This version of the code only does GC of data and code. GC for the types section is coming soon. Differential Revision: https://reviews.llvm.org/D42511 git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@323842 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/wasm/call-indirect.ll | 2 +- test/wasm/comdats.ll | 2 +- test/wasm/data-layout.ll | 2 +- test/wasm/entry.ll | 2 +- test/wasm/gc-sections.ll | 79 +++++++++++++++++++++++++++ test/wasm/local-symbols.ll | 8 +-- test/wasm/weak-symbols.ll | 2 +- wasm/CMakeLists.txt | 1 + wasm/Config.h | 2 + wasm/Driver.cpp | 24 +++++++-- wasm/InputChunks.h | 10 +++- wasm/InputFiles.cpp | 8 +-- wasm/InputFiles.h | 2 + wasm/MarkLive.cpp | 107 +++++++++++++++++++++++++++++++++++++ wasm/MarkLive.h | 20 +++++++ wasm/Options.td | 12 +++++ wasm/Writer.cpp | 7 ++- 17 files changed, 269 insertions(+), 21 deletions(-) create mode 100644 test/wasm/gc-sections.ll create mode 100644 wasm/MarkLive.cpp create mode 100644 wasm/MarkLive.h diff --git a/test/wasm/call-indirect.ll b/test/wasm/call-indirect.ll index 506d092f2..45270f6cc 100644 --- a/test/wasm/call-indirect.ll +++ b/test/wasm/call-indirect.ll @@ -1,6 +1,6 @@ ; RUN: llc -filetype=obj %p/Inputs/call-indirect.ll -o %t2.o ; RUN: llc -filetype=obj %s -o %t.o -; RUN: lld -flavor wasm -o %t.wasm %t2.o %t.o +; RUN: lld -flavor wasm -no-gc-sections -o %t.wasm %t2.o %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s ; bitcode generated from the following C code: diff --git a/test/wasm/comdats.ll b/test/wasm/comdats.ll index bc4c6ea53..6871c02b1 100644 --- a/test/wasm/comdats.ll +++ b/test/wasm/comdats.ll @@ -1,7 +1,7 @@ ; 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: lld -flavor wasm -no-gc-sections -o %t.wasm %t.o %t1.o %t2.o ; RUN: obj2yaml %t.wasm | FileCheck %s target triple = "wasm32-unknown-unknown-wasm" diff --git a/test/wasm/data-layout.ll b/test/wasm/data-layout.ll index 68e81e8da..efd4e0870 100644 --- a/test/wasm/data-layout.ll +++ b/test/wasm/data-layout.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-unknown-wasm" @hello_str = external global i8* @external_ref = global i8** @hello_str, align 8 -; RUN: lld -flavor wasm --allow-undefined -o %t.wasm %t.o %t.hello.o +; RUN: lld -flavor wasm -no-gc-sections --allow-undefined -o %t.wasm %t.o %t.hello.o ; RUN: obj2yaml %t.wasm | FileCheck %s ; CHECK: - Type: GLOBAL diff --git a/test/wasm/entry.ll b/test/wasm/entry.ll index f77ed381e..a84d37c4b 100644 --- a/test/wasm/entry.ll +++ b/test/wasm/entry.ll @@ -33,4 +33,4 @@ entry: ; CHECK-CTOR-NEXT: Index: 0 ; CHECK-CTOR-NEXT: - Name: __wasm_call_ctors ; CHECK-CTOR-NEXT: Kind: FUNCTION -; CHECK-CTOR-NEXT: Index: 1 +; CHECK-CTOR-NEXT: Index: 0 diff --git a/test/wasm/gc-sections.ll b/test/wasm/gc-sections.ll new file mode 100644 index 000000000..fa9888acb --- /dev/null +++ b/test/wasm/gc-sections.ll @@ -0,0 +1,79 @@ +; RUN: llc -filetype=obj %s -o %t.o +; RUN: lld -flavor wasm -print-gc-sections -o %t1.wasm %t.o | FileCheck %s -check-prefix=PRINT-GC +; PRINT-GC: removing unused section 'unused_function' in file '{{.*}}' +; PRINT-GC-NOT: removing unused section 'used_function' in file '{{.*}}' +; PRINT-GC: removing unused section '.data.unused_data' in file '{{.*}}' +; PRINT-GC-NOT: removing unused section '.data.used_data' in file '{{.*}}' + +target triple = "wasm32-unknown-unknown-wasm" + +@unused_data = hidden global i32 1, align 4 +@used_data = hidden global i32 2, align 4 + +define hidden i32 @unused_function() { + %1 = load i32, i32* @unused_data, align 4 + ret i32 %1 +} + +define hidden i32 @used_function() { + %1 = load i32, i32* @used_data, align 4 + ret i32 %1 +} + +define hidden void @_start() { +entry: + call i32 @used_function() + ret void +} + +; RUN: obj2yaml %t1.wasm | FileCheck %s +; CHECK: - 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: '02000000' +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: linking +; CHECK-NEXT: DataSize: 4 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: name +; CHECK-NEXT: FunctionNames: +; CHECK-NEXT: - Index: 0 +; CHECK-NEXT: Name: used_function +; CHECK-NEXT: - Index: 1 +; CHECK-NEXT: Name: _start +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: Name: __wasm_call_ctors +; CHECK-NEXT: ... + +; RUN: lld -flavor wasm -print-gc-sections --no-gc-sections -o %t1.no-gc.wasm %t.o +; RUN: obj2yaml %t1.no-gc.wasm | FileCheck %s -check-prefix=NO-GC +; NO-GC: - Type: DATA +; NO-GC-NEXT: Segments: +; NO-GC-NEXT: - SectionOffset: 7 +; NO-GC-NEXT: MemoryIndex: 0 +; NO-GC-NEXT: Offset: +; NO-GC-NEXT: Opcode: I32_CONST +; NO-GC-NEXT: Value: 1024 +; NO-GC-NEXT: Content: '0100000002000000' +; NO-GC-NEXT: - Type: CUSTOM +; NO-GC-NEXT: Name: linking +; NO-GC-NEXT: DataSize: 8 +; NO-GC-NEXT: - Type: CUSTOM +; NO-GC-NEXT: Name: name +; NO-GC-NEXT: FunctionNames: +; NO-GC-NEXT: - Index: 0 +; NO-GC-NEXT: Name: unused_function +; NO-GC-NEXT: - Index: 1 +; NO-GC-NEXT: Name: used_function +; NO-GC-NEXT: - Index: 2 +; NO-GC-NEXT: Name: _start +; NO-GC-NEXT: - Index: 3 +; NO-GC-NEXT: Name: __wasm_call_ctors +; NO-GC-NEXT: ... + +; RUN: not lld -flavor wasm --gc-sections --relocatable -o %t1.no-gc.wasm %t.o 2>&1 | FileCheck %s -check-prefix=CHECK-ERROR +; CHECK-ERROR: lld: error: -r and --gc-sections may not be used together diff --git a/test/wasm/local-symbols.ll b/test/wasm/local-symbols.ll index 00c0bd323..65bf13482 100644 --- a/test/wasm/local-symbols.ll +++ b/test/wasm/local-symbols.ll @@ -9,11 +9,13 @@ target triple = "wasm32-unknown-unknown-wasm" define internal i32 @baz() local_unnamed_addr { entry: - ret i32 2 + %0 = load i32, i32* @bar, align 4 + ret i32 %0 } define i32 @_start() local_unnamed_addr { entry: + call i32 @baz() ret i32 1 } @@ -70,10 +72,10 @@ entry: ; CHECK-NEXT: Functions: ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Locals: -; CHECK-NEXT: Body: 41020B +; CHECK-NEXT: Body: 4100280284888080000B ; CHECK-NEXT: - Index: 1 ; CHECK-NEXT: Locals: -; CHECK-NEXT: Body: 41010B +; CHECK-NEXT: Body: 1080808080001A41010B ; CHECK-NEXT: - Index: 2 ; CHECK-NEXT: Locals: ; CHECK-NEXT: Body: 0B diff --git a/test/wasm/weak-symbols.ll b/test/wasm/weak-symbols.ll index 8a86c1cf4..b29257302 100644 --- a/test/wasm/weak-symbols.ll +++ b/test/wasm/weak-symbols.ll @@ -1,7 +1,7 @@ ; RUN: llc -filetype=obj %p/Inputs/weak-symbol1.ll -o %t1.o ; RUN: llc -filetype=obj %p/Inputs/weak-symbol2.ll -o %t2.o ; RUN: llc -filetype=obj %s -o %t.o -; RUN: lld -flavor wasm -o %t.wasm %t.o %t1.o %t2.o +; RUN: lld -flavor wasm -no-gc-sections -o %t.wasm %t.o %t1.o %t2.o ; RUN: obj2yaml %t.wasm | FileCheck %s target triple = "wasm32-unknown-unknown-wasm" diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt index 927c9c57f..d3bef0f62 100644 --- a/wasm/CMakeLists.txt +++ b/wasm/CMakeLists.txt @@ -6,6 +6,7 @@ add_lld_library(lldWasm Driver.cpp InputChunks.cpp InputFiles.cpp + MarkLive.cpp OutputSections.cpp SymbolTable.cpp Symbols.cpp diff --git a/wasm/Config.h b/wasm/Config.h index 4b1ff2554..3ac229707 100644 --- a/wasm/Config.h +++ b/wasm/Config.h @@ -23,7 +23,9 @@ struct Configuration { bool AllowUndefined; bool CheckSignatures; bool Demangle; + bool GcSections; bool ImportMemory; + bool PrintGcSections; bool Relocatable; bool StripAll; bool StripDebug; diff --git a/wasm/Driver.cpp b/wasm/Driver.cpp index b32240ef7..24079d004 100644 --- a/wasm/Driver.cpp +++ b/wasm/Driver.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// #include "lld/Common/Driver.h" -#include "Config.h" +#include "InputChunks.h" +#include "MarkLive.h" #include "SymbolTable.h" #include "Writer.h" #include "lld/Common/Args.h" @@ -23,6 +24,8 @@ #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#define DEBUG_TYPE "lld" + using namespace llvm; using namespace llvm::sys; using namespace llvm::wasm; @@ -253,6 +256,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->ImportMemory = Args.hasArg(OPT_import_memory); Config->OutputFile = Args.getLastArgValue(OPT_o); Config->Relocatable = Args.hasArg(OPT_relocatable); + Config->GcSections = + Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, !Config->Relocatable); + Config->PrintGcSections = + Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); Config->SearchPaths = args::getStrings(Args, OPT_L); Config->StripAll = Args.hasArg(OPT_strip_all); Config->StripDebug = Args.hasArg(OPT_strip_debug); @@ -274,10 +281,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (!Args.hasArg(OPT_INPUT)) error("no input files"); - if (Config->Relocatable && !Config->Entry.empty()) - error("entry point specified for relocatable output file"); - if (Config->Relocatable && Args.hasArg(OPT_undefined)) - error("undefined symbols specified for relocatable output file"); + if (Config->Relocatable) { + if (!Config->Entry.empty()) + error("entry point specified for relocatable output file"); + if (Config->GcSections) + error("-r and --gc-sections may not be used together"); + if (Args.hasArg(OPT_undefined)) + error("-r -and --undefined may not be used together"); + } Symbol *EntrySym = nullptr; if (!Config->Relocatable) { @@ -345,6 +356,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (errorCount()) return; + // Do size optimizations: garbage collection + markLive(); + // Write the result to the file. writeResult(); } diff --git a/wasm/InputChunks.h b/wasm/InputChunks.h index 45ea677a3..434680a1a 100644 --- a/wasm/InputChunks.h +++ b/wasm/InputChunks.h @@ -15,6 +15,7 @@ #ifndef LLD_WASM_INPUT_CHUNKS_H #define LLD_WASM_INPUT_CHUNKS_H +#include "Config.h" #include "InputFiles.h" #include "WriterUtils.h" #include "lld/Common/ErrorHandler.h" @@ -58,9 +59,15 @@ class InputChunk { bool Discarded = false; std::vector<OutputRelocation> OutRelocations; + const ObjFile *File; + + // The garbage collector sets sections' Live bits. + // If GC is disabled, all sections are considered live by default. + unsigned Live : 1; protected: - InputChunk(const ObjFile *F, Kind K) : File(F), SectionKind(K) {} + InputChunk(const ObjFile *F, Kind K) + : File(F), Live(!Config->GcSections), SectionKind(K) {} virtual ~InputChunk() = default; void calcRelocations(); virtual ArrayRef<uint8_t> data() const = 0; @@ -68,7 +75,6 @@ class InputChunk { std::vector<WasmRelocation> Relocations; int32_t OutputOffset = 0; - const ObjFile *File; Kind SectionKind; }; diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp index 8388c718f..3c5d66623 100644 --- a/wasm/InputFiles.cpp +++ b/wasm/InputFiles.cpp @@ -51,11 +51,11 @@ void ObjFile::dumpInfo() const { } uint32_t ObjFile::relocateVirtualAddress(uint32_t GlobalIndex) const { - return GlobalSymbols[GlobalIndex]->getVirtualAddress(); + return getGlobalSymbol(GlobalIndex)->getVirtualAddress(); } uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const { - Symbol *Sym = FunctionSymbols[Original]; + const Symbol *Sym = getFunctionSymbol(Original); uint32_t Index = Sym->getOutputIndex(); DEBUG(dbgs() << "relocateFunctionIndex: " << toString(*Sym) << ": " << Original << " -> " << Index << "\n"); @@ -67,7 +67,7 @@ uint32_t ObjFile::relocateTypeIndex(uint32_t Original) const { } uint32_t ObjFile::relocateTableIndex(uint32_t Original) const { - Symbol *Sym = FunctionSymbols[Original]; + const Symbol *Sym = getFunctionSymbol(Original); uint32_t Index = Sym->hasTableIndex() ? Sym->getTableIndex() : 0; DEBUG(dbgs() << "relocateTableIndex: " << toString(*Sym) << ": " << Original << " -> " << Index << "\n"); @@ -75,7 +75,7 @@ uint32_t ObjFile::relocateTableIndex(uint32_t Original) const { } uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const { - Symbol *Sym = GlobalSymbols[Original]; + const Symbol *Sym = getGlobalSymbol(Original); uint32_t Index = Sym->hasOutputIndex() ? Sym->getOutputIndex() : 0; DEBUG(dbgs() << "relocateGlobalIndex: " << toString(*Sym) << ": " << Original << " -> " << Index << "\n"); diff --git a/wasm/InputFiles.h b/wasm/InputFiles.h index 7828c8cfe..39472312c 100644 --- a/wasm/InputFiles.h +++ b/wasm/InputFiles.h @@ -110,6 +110,8 @@ class ObjFile : public InputFile { return FunctionSymbols[Index]; } + Symbol *getGlobalSymbol(uint32_t Index) const { return GlobalSymbols[Index]; } + private: uint32_t relocateVirtualAddress(uint32_t Index) const; uint32_t relocateTypeIndex(uint32_t Original) const; diff --git a/wasm/MarkLive.cpp b/wasm/MarkLive.cpp new file mode 100644 index 000000000..9bd2a83c0 --- /dev/null +++ b/wasm/MarkLive.cpp @@ -0,0 +1,107 @@ +//===- MarkLive.cpp -------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements --gc-sections, which is a feature to remove unused +// chunks from the output. Unused chunks are those that are not reachable from +// known root symbols or chunks. This feature is implemented as a mark-sweep +// garbage collector. +// +// Here's how it works. Each InputChunk has a "Live" bit. The bit is off by +// default. Starting with the GC-roots, visit all reachable chunks and set their +// Live bits. The Writer will then ignore chunks whose Live bits are off, so +// that such chunk are not appear in the output. +// +//===----------------------------------------------------------------------===// + +#include "MarkLive.h" +#include "Config.h" +#include "InputChunks.h" +#include "SymbolTable.h" +#include "Symbols.h" + +#define DEBUG_TYPE "lld" + +using namespace llvm; +using namespace llvm::wasm; +using namespace lld; +using namespace lld::wasm; + +void lld::wasm::markLive() { + if (!Config->GcSections) + return; + + DEBUG(dbgs() << "markLive\n"); + SmallVector<InputChunk *, 256> Q; + + auto Enqueue = [&](Symbol *Sym) { + if (!Sym) + return; + InputChunk *Chunk = Sym->getChunk(); + if (!Chunk || Chunk->Live) + return; + Chunk->Live = true; + Q.push_back(Chunk); + }; + + // Add GC root symbols. + if (!Config->Entry.empty()) + Enqueue(Symtab->find(Config->Entry)); + Enqueue(Config->CtorSymbol); + + // By default we export all non-hidden, so they are gc roots too + for (Symbol *Sym : Symtab->getSymbols()) + if (!Sym->isHidden()) + Enqueue(Sym); + + // The ctor fuctions are all used the synthetic __wasm_call_ctors function, + // but since this function is created in-place it doesn't contain reloctations + // which mean we have to manually mark the ctors. + for (const ObjFile *Obj : Symtab->ObjectFiles) { + const WasmLinkingData &L = Obj->getWasmObj()->linkingData(); + for (const WasmInitFunc &F : L.InitFunctions) + Enqueue(Obj->getFunctionSymbol(F.FunctionIndex)); + } + + auto EnqueueSuccessors = [Enqueue](InputChunk &Chunk) { + for (const WasmRelocation Reloc : Chunk.getRelocations()) { + switch (Reloc.Type) { + case R_WEBASSEMBLY_FUNCTION_INDEX_LEB: + case R_WEBASSEMBLY_TABLE_INDEX_I32: + case R_WEBASSEMBLY_TABLE_INDEX_SLEB: + Enqueue(Chunk.File->getFunctionSymbol(Reloc.Index)); + break; + case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_LEB: + case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: + case R_WEBASSEMBLY_MEMORY_ADDR_I32: + Enqueue(Chunk.File->getGlobalSymbol(Reloc.Index)); + break; + } + } + }; + + while (!Q.empty()) + EnqueueSuccessors(*Q.pop_back_val()); + + // Report garbage-collected sections. + if (Config->PrintGcSections) { + auto CheckChunk = [](const InputChunk *C) { + if (!C->Live) + message("removing unused section '" + C->getName() + "' in file '" + + C->getFileName() + "'"); + }; + + for (const ObjFile *Obj : Symtab->ObjectFiles) { + for (InputChunk *C : Obj->Functions) + CheckChunk(C); + for (InputChunk *C : Obj->Segments) + CheckChunk(C); + } + } +} diff --git a/wasm/MarkLive.h b/wasm/MarkLive.h new file mode 100644 index 000000000..6fc05635e --- /dev/null +++ b/wasm/MarkLive.h @@ -0,0 +1,20 @@ +//===- MarkLive.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_WASM_MARKLIVE_H +#define LLD_WASM_MARKLIVE_H + +namespace lld { +namespace wasm { + +void markLive(); +} +} // namespace lld + +#endif // LLD_WASM_MARKLIVE_H diff --git a/wasm/Options.td b/wasm/Options.td index fbedcd6cc..6260d4d98 100644 --- a/wasm/Options.td +++ b/wasm/Options.td @@ -27,6 +27,9 @@ def error_limit: J<"error-limit=">, def fatal_warnings: F<"fatal-warnings">, HelpText<"Treat warnings as errors">; +def gc_sections: F<"gc-sections">, + HelpText<"Enable garbage collection of unused sections">; + def help: F<"help">, HelpText<"Print option help">; def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">, @@ -45,9 +48,18 @@ def no_color_diagnostics: F<"no-color-diagnostics">, def no_fatal_warnings: F<"no-fatal-warnings">; +def no_gc_sections: F<"no-gc-sections">, + HelpText<"Disable garbage collection of unused sections">; + +def no_print_gc_sections: F<"no-print-gc-sections">, + HelpText<"Do not list removed unused sections">; + def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">, HelpText<"Path to file to write output">; +def print_gc_sections: F<"print-gc-sections">, + HelpText<"List removed unused sections">; + def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">; def strip_all: F<"strip-all">, HelpText<"Strip all symbols">; diff --git a/wasm/Writer.cpp b/wasm/Writer.cpp index 57f4e51dc..0e051b763 100644 --- a/wasm/Writer.cpp +++ b/wasm/Writer.cpp @@ -653,6 +653,9 @@ void Writer::calculateExports() { if ((Sym->isHidden() || Sym->isLocal()) && !ExportHidden) continue; + + // We should never be exporting a non-live symbol + assert(Sym->getChunk()->Live); ExportedSymbols.emplace_back(WasmExportEntry{Sym, BudgeLocalName(Sym)}); } } @@ -735,7 +738,7 @@ void Writer::assignIndexes() { for (ObjFile *File : Symtab->ObjectFiles) { DEBUG(dbgs() << "Functions: " << File->getName() << "\n"); for (InputFunction *Func : File->Functions) { - if (Func->Discarded) + if (Func->Discarded || !Func->Live) continue; DefinedFunctions.emplace_back(Func); Func->setOutputIndex(FunctionIndex++); @@ -784,7 +787,7 @@ static StringRef getOutputDataSegmentName(StringRef Name) { void Writer::createOutputSegments() { for (ObjFile *File : Symtab->ObjectFiles) { for (InputSegment *Segment : File->Segments) { - if (Segment->Discarded) + if (Segment->Discarded || !Segment->Live) continue; StringRef Name = getOutputDataSegmentName(Segment->getName()); OutputSegment *&S = SegmentMap[Name];