Add the /order option.

With the /order option, you can give an order file. An order file
contains symbol names, one per line, and the linker places comdat
sections in that given order. The option is used often to optimize
an output binary for (in particular, startup) speed by improving
locality.

Differential Revision: https://reviews.llvm.org/D42598

llvm-svn: 323579
This commit is contained in:
Rui Ueyama
2018-01-27 00:34:46 +00:00
parent e5dbb64652
commit 57175aa1e9
7 changed files with 304 additions and 0 deletions

View File

@@ -10,6 +10,7 @@
#ifndef LLD_COFF_CONFIG_H
#define LLD_COFF_CONFIG_H
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/CachePruning.h"
@@ -153,6 +154,9 @@ struct Configuration {
// Used for /alternatename.
std::map<StringRef, StringRef> AlternateNames;
// Used for /order.
llvm::StringMap<int> Order;
// Used for /lldmap.
std::string MapFile;

View File

@@ -14,6 +14,7 @@
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
@@ -756,6 +757,33 @@ bool LinkerDriver::run() {
return DidWork;
}
// Parse an /order file. If an option is given, the linker places
// COMDAT sections in the same order as their names appear in the
// given file.
static void parseOrderFile(StringRef Arg) {
// For some reason, the MSVC linker requires a filename to be
// preceded by "@".
if (!Arg.startswith("@")) {
error("malformed /order option: '@' missing");
return;
}
// Open a file.
StringRef Path = Arg.substr(1);
std::unique_ptr<MemoryBuffer> MB = CHECK(
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
// Parse a file. An order file contains one symbol per line.
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
for (std::string S : args::getLines(MB->getMemBufferRef())) {
if (Config->Machine == I386 && !isDecorated(S))
S = "_" + S;
Config->Order[S] = INT_MIN + Config->Order.size();
}
}
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// If the first command line argument is "/lib", link.exe acts like lib.exe.
// We call our own implementation of lib.exe that understands bitcode files.
@@ -1160,6 +1188,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
}
// Handle /order
if (auto *Arg = Args.getLastArg(OPT_order))
parseOrderFile(Arg->getValue());
// Handle /export
for (auto *Arg : Args.filtered(OPT_export)) {
Export E = parseExport(Arg->getValue());

View File

@@ -42,6 +42,7 @@ def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
def opt : P<"opt", "Control optimizations">;
def order : P<"order", "Put functions in order">;
def out : P<"out", "Path to file to write output">;
def pdb : P<"pdb", "PDB file path">;
def section : P<"section", "Specify section attributes">;

View File

@@ -348,6 +348,21 @@ static StringRef getOutputSection(StringRef Name) {
return It->second;
}
// For /order.
static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
auto GetPriority = [](const Chunk *C) {
if (auto *Sec = dyn_cast<SectionChunk>(C))
if (Sec->Sym)
return Config->Order.lookup(Sec->Sym->getName());
return 0;
};
std::stable_sort(Chunks.begin(), Chunks.end(),
[=](const Chunk *A, const Chunk *B) {
return GetPriority(A) < GetPriority(B);
});
}
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, bin chunks by name.
@@ -362,6 +377,11 @@ void Writer::createSections() {
Map[C->getSectionName()].push_back(C);
}
// Process an /order option.
if (!Config->Order.empty())
for (auto &Pair : Map)
sortBySectionOrder(Pair.second);
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo

View File

@@ -0,0 +1,76 @@
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: unrelated2
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: .text
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 2
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: fn4
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: .text
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 3
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: fn1
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

View File

@@ -0,0 +1,69 @@
# RUN: yaml2obj < %s > %t.obj
# RUN: echo fn1 > %t.order
# RUN: echo fn2 >> %t.order
# RUN: lld-link -entry:fn1 -subsystem:console -opt:noref %t.obj \
# RUN: -lldmap:- -out:%t.exe -order:@%t.order | FileCheck %s
# CHECK: fn1
# CHECK: fn2
# RUN: lld-link -entry:fn1 -subsystem:console -opt:noref %t.obj \
# RUN: -lldmap:- -out:%t.exe | FileCheck -check-prefix=DEFAULT %s
# DEFAULT: fn2
# DEFAULT: fn1
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_I386
Characteristics: [ ]
sections:
- Name: '.text'
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: CC
- Name: '.text'
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: CC
symbols:
- Name: '.text'
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: '.text'
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: _fn2
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: _fn1
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...

102
lld/test/COFF/order.test Normal file
View File

@@ -0,0 +1,102 @@
# RUN: yaml2obj < %s > %t1.obj
# RUN: yaml2obj < %p/Inputs/order.yaml > %t2.obj
# RUN: echo fn1 > %t.order
# RUN: echo fn2 >> %t.order
# RUN: echo fn3 >> %t.order
# RUN: echo fn4 >> %t.order
# RUN: lld-link -entry:fn1 -subsystem:console -opt:noref -debug %t1.obj %t2.obj \
# RUN: -lldmap:- -out:%t.exe -order:@%t.order | FileCheck %s
# CHECK: fn1
# CHECK: fn2
# CHECK: fn3
# CHECK: fn4
# CHECK: unrelated1
# CHECK: unrelated2
# RUN: lld-link -entry:fn1 -subsystem:console -opt:noref -debug %t1.obj %t2.obj \
# RUN: -lldmap:- -out:%t.exe | FileCheck -check-prefix=DEFAULT %s
# DEFAULT: fn2
# DEFAULT: fn3
# DEFAULT: unrelated1
# DEFAULT: unrelated2
# DEFAULT: fn4
# DEFAULT: fn1
--- !COFF
header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: C3
symbols:
- Name: .text
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 1
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: fn2
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: .text
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 2
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: fn3
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: .text
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
CheckSum: 0
Number: 3
Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
- Name: unrelated1
Value: 0
SectionNumber: 3
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...