This is a pretty big refactoring diff, so here are the motivations: Previously, ICF ran after scanRelocations(), where we emitting bind/rebase opcodes etc. So we had a bunch of redundant leftovers after ICF. Having ICF run before Writer seems like a better design, and is what LLD-ELF does, so this diff refactors it accordingly. However, ICF had two dependencies on things occurring in Writer: 1) it needs literals to be deduplicated beforehand and 2) it needs to know which functions have unwind info, which was being handled by `UnwindInfoSection::prepareRelocations()`. In order to do literal deduplication earlier, we need to add literal input sections to their corresponding output sections. So instead of putting all input sections into the big `inputSections` vector, and then filtering them by type later on, I've changed things so that literal sections get added directly to their output sections during the 'gather' phase. Likewise for compact unwind sections -- they get added directly to the UnwindInfoSection now. This latter change is not strictly necessary, but makes it easier for ICF to determine which functions have unwind info. Adding literal sections directly to their output sections means that we can no longer determine `inputOrder` from iterating over `inputSections`. Instead, we store that order explicitly on InputSection. Bloating the size of InputSection for this purpose would be unfortunate -- but LLD-ELF has already solved this problem: it reuses `outSecOff` to store this order value. One downside of this refactor is that we now make an additional pass over the unwind info relocations to figure out which functions have unwind info, since want to know that before `processRelocations()`. I've made sure to run that extra loop only if ICF is enabled, so there should be no overhead in non-optimizing runs of the linker. The upside of all this is that the `inputSections` vector now contains only ConcatInputSections that are destined for ConcatOutputSections, so we can clean up a bunch of code that just existed to filter out other elements from that vector. I will test for the lack of redundant binds/rebases in the upcoming cfstring deduplication diff. While binds/rebases can also happen in the regular `.text` section, they're more common in `.data` sections, so it seems more natural to test it that way. This change is perf-neutral when linking chromium_framework. Reviewed By: oontvoo Differential Revision: https://reviews.llvm.org/D105044
88 lines
2.9 KiB
C++
88 lines
2.9 KiB
C++
//===- ConcatOutputSection.h ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLD_MACHO_MERGED_OUTPUT_SECTION_H
|
|
#define LLD_MACHO_MERGED_OUTPUT_SECTION_H
|
|
|
|
#include "InputSection.h"
|
|
#include "OutputSection.h"
|
|
#include "lld/Common/LLVM.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
|
|
namespace lld {
|
|
namespace macho {
|
|
|
|
class Defined;
|
|
|
|
// Linking multiple files will inevitably mean resolving sections in different
|
|
// files that are labeled with the same segment and section name. This class
|
|
// contains all such sections and writes the data from each section sequentially
|
|
// in the final binary.
|
|
class ConcatOutputSection final : public OutputSection {
|
|
public:
|
|
explicit ConcatOutputSection(StringRef name)
|
|
: OutputSection(ConcatKind, name) {}
|
|
|
|
const ConcatInputSection *firstSection() const { return inputs.front(); }
|
|
const ConcatInputSection *lastSection() const { return inputs.back(); }
|
|
|
|
// These accessors will only be valid after finalizing the section
|
|
uint64_t getSize() const override { return size; }
|
|
uint64_t getFileSize() const override { return fileSize; }
|
|
|
|
void addInput(ConcatInputSection *input);
|
|
void finalize() override;
|
|
bool needsThunks() const;
|
|
uint64_t estimateStubsInRangeVA(size_t callIdx) const;
|
|
|
|
void writeTo(uint8_t *buf) const override;
|
|
|
|
std::vector<ConcatInputSection *> inputs;
|
|
std::vector<ConcatInputSection *> thunks;
|
|
|
|
static bool classof(const OutputSection *sec) {
|
|
return sec->kind() == ConcatKind;
|
|
}
|
|
|
|
private:
|
|
void finalizeFlags(InputSection *input);
|
|
|
|
size_t size = 0;
|
|
uint64_t fileSize = 0;
|
|
};
|
|
|
|
// We maintain one ThunkInfo per real function.
|
|
//
|
|
// The "active thunk" is represented by the sym/isec pair that
|
|
// turns-over during finalize(): as the call-site address advances,
|
|
// the active thunk goes out of branch-range, and we create a new
|
|
// thunk to take its place.
|
|
//
|
|
// The remaining members -- bools and counters -- apply to the
|
|
// collection of thunks associated with the real function.
|
|
|
|
struct ThunkInfo {
|
|
// These denote the active thunk:
|
|
Defined *sym = nullptr; // private-extern symbol for active thunk
|
|
ConcatInputSection *isec = nullptr; // input section for active thunk
|
|
|
|
// The following values are cumulative across all thunks on this function
|
|
uint32_t callSiteCount = 0; // how many calls to the real function?
|
|
uint32_t callSitesUsed = 0; // how many call sites processed so-far?
|
|
uint32_t thunkCallCount = 0; // how many call sites went to thunk?
|
|
uint8_t sequence = 0; // how many thunks created so-far?
|
|
};
|
|
|
|
extern llvm::DenseMap<Symbol *, ThunkInfo> thunkMap;
|
|
|
|
} // namespace macho
|
|
} // namespace lld
|
|
|
|
#endif
|