
Apologies for the large change, I looked for ways to break this up and all of the ones I saw added real complexity. This change focuses on the option's prefixed names and the array of prefixes. These are present in every option and the dominant source of dynamic relocations for PIE or PIC users of LLVM and Clang tooling. In some cases, 100s or 1000s of them for the Clang driver which has a huge number of options. This PR addresses this by building a string table and a prefixes table that can be referenced with indices rather than pointers that require dynamic relocations. This removes almost 7k dynmaic relocations from the `clang` binary, roughly 8% of the remaining dynmaic relocations outside of vtables. For busy-boxing use cases where many different option tables are linked into the same binary, the savings add up a bit more. The string table is a straightforward mechanism, but the prefixes required some subtlety. They are encoded in a Pascal-string fashion with a size followed by a sequence of offsets. This works relatively well for the small realistic prefixes arrays in use. Lots of code has to change in order to land this though: both all the option library code has to be updated to use the string table and prefixes table, and all the users of the options library have to be updated to correctly instantiate the objects. Some follow-up patches in the works to provide an abstraction for this style of code, and to start using the same technique for some of the other strings here now that the infrastructure is in place.
297 lines
9.6 KiB
C++
297 lines
9.6 KiB
C++
//===- Option.cpp - Abstract Driver Options -------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Option/Option.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Config/llvm-config.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Option/OptTable.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::opt;
|
|
|
|
Option::Option(const OptTable::Info *info, const OptTable *owner)
|
|
: Info(info), Owner(owner) {
|
|
// Multi-level aliases are not supported. This just simplifies option
|
|
// tracking, it is not an inherent limitation.
|
|
assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
|
|
"Multi-level aliases are not supported.");
|
|
|
|
if (Info && getAliasArgs()) {
|
|
assert(getAlias().isValid() && "Only alias options can have alias args.");
|
|
assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
|
|
assert(getAlias().getKind() != FlagClass &&
|
|
"Cannot provide alias args to a flag option.");
|
|
}
|
|
}
|
|
|
|
void Option::print(raw_ostream &O, bool AddNewLine) const {
|
|
O << "<";
|
|
switch (getKind()) {
|
|
#define P(N) case N: O << #N; break
|
|
P(GroupClass);
|
|
P(InputClass);
|
|
P(UnknownClass);
|
|
P(FlagClass);
|
|
P(JoinedClass);
|
|
P(ValuesClass);
|
|
P(SeparateClass);
|
|
P(CommaJoinedClass);
|
|
P(MultiArgClass);
|
|
P(JoinedOrSeparateClass);
|
|
P(JoinedAndSeparateClass);
|
|
P(RemainingArgsClass);
|
|
P(RemainingArgsJoinedClass);
|
|
#undef P
|
|
}
|
|
|
|
if (!Info->hasNoPrefix()) {
|
|
O << " Prefixes:[";
|
|
for (size_t I = 0, N = Info->getNumPrefixes(Owner->getPrefixesTable());
|
|
I != N; ++I)
|
|
O << '"'
|
|
<< Info->getPrefix(Owner->getStrTable(), Owner->getPrefixesTable(), I)
|
|
<< (I == N - 1 ? "\"" : "\", ");
|
|
O << ']';
|
|
}
|
|
|
|
O << " Name:\"" << getName() << '"';
|
|
|
|
const Option Group = getGroup();
|
|
if (Group.isValid()) {
|
|
O << " Group:";
|
|
Group.print(O, /*AddNewLine=*/false);
|
|
}
|
|
|
|
const Option Alias = getAlias();
|
|
if (Alias.isValid()) {
|
|
O << " Alias:";
|
|
Alias.print(O, /*AddNewLine=*/false);
|
|
}
|
|
|
|
if (getKind() == MultiArgClass)
|
|
O << " NumArgs:" << getNumArgs();
|
|
|
|
O << ">";
|
|
if (AddNewLine)
|
|
O << "\n";
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void Option::dump() const { print(dbgs()); }
|
|
#endif
|
|
|
|
bool Option::matches(OptSpecifier Opt) const {
|
|
// Aliases are never considered in matching, look through them.
|
|
const Option Alias = getAlias();
|
|
if (Alias.isValid())
|
|
return Alias.matches(Opt);
|
|
|
|
// Check exact match.
|
|
if (getID() == Opt.getID())
|
|
return true;
|
|
|
|
const Option Group = getGroup();
|
|
if (Group.isValid())
|
|
return Group.matches(Opt);
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<Arg> Option::acceptInternal(const ArgList &Args,
|
|
StringRef Spelling,
|
|
unsigned &Index) const {
|
|
const size_t SpellingSize = Spelling.size();
|
|
const size_t ArgStringSize = StringRef(Args.getArgString(Index)).size();
|
|
switch (getKind()) {
|
|
case FlagClass: {
|
|
if (SpellingSize != ArgStringSize)
|
|
return nullptr;
|
|
return std::make_unique<Arg>(*this, Spelling, Index++);
|
|
}
|
|
case JoinedClass: {
|
|
const char *Value = Args.getArgString(Index) + SpellingSize;
|
|
return std::make_unique<Arg>(*this, Spelling, Index++, Value);
|
|
}
|
|
case CommaJoinedClass: {
|
|
// Always matches.
|
|
const char *Str = Args.getArgString(Index) + SpellingSize;
|
|
auto A = std::make_unique<Arg>(*this, Spelling, Index++);
|
|
|
|
// Parse out the comma separated values.
|
|
const char *Prev = Str;
|
|
for (;; ++Str) {
|
|
char c = *Str;
|
|
|
|
if (!c || c == ',') {
|
|
if (Prev != Str) {
|
|
char *Value = new char[Str - Prev + 1];
|
|
memcpy(Value, Prev, Str - Prev);
|
|
Value[Str - Prev] = '\0';
|
|
A->getValues().push_back(Value);
|
|
}
|
|
|
|
if (!c)
|
|
break;
|
|
|
|
Prev = Str + 1;
|
|
}
|
|
}
|
|
A->setOwnsValues(true);
|
|
|
|
return A;
|
|
}
|
|
case SeparateClass:
|
|
// Matches iff this is an exact match.
|
|
if (SpellingSize != ArgStringSize)
|
|
return nullptr;
|
|
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return std::make_unique<Arg>(*this, Spelling, Index - 2,
|
|
Args.getArgString(Index - 1));
|
|
case MultiArgClass: {
|
|
// Matches iff this is an exact match.
|
|
if (SpellingSize != ArgStringSize)
|
|
return nullptr;
|
|
|
|
Index += 1 + getNumArgs();
|
|
if (Index > Args.getNumInputArgStrings())
|
|
return nullptr;
|
|
|
|
auto A = std::make_unique<Arg>(*this, Spelling, Index - 1 - getNumArgs(),
|
|
Args.getArgString(Index - getNumArgs()));
|
|
for (unsigned i = 1; i != getNumArgs(); ++i)
|
|
A->getValues().push_back(Args.getArgString(Index - getNumArgs() + i));
|
|
return A;
|
|
}
|
|
case JoinedOrSeparateClass: {
|
|
// If this is not an exact match, it is a joined arg.
|
|
if (SpellingSize != ArgStringSize) {
|
|
const char *Value = Args.getArgString(Index) + SpellingSize;
|
|
return std::make_unique<Arg>(*this, Spelling, Index++, Value);
|
|
}
|
|
|
|
// Otherwise it must be separate.
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return std::make_unique<Arg>(*this, Spelling, Index - 2,
|
|
Args.getArgString(Index - 1));
|
|
}
|
|
case JoinedAndSeparateClass:
|
|
// Always matches.
|
|
Index += 2;
|
|
if (Index > Args.getNumInputArgStrings() ||
|
|
Args.getArgString(Index - 1) == nullptr)
|
|
return nullptr;
|
|
|
|
return std::make_unique<Arg>(*this, Spelling, Index - 2,
|
|
Args.getArgString(Index - 2) + SpellingSize,
|
|
Args.getArgString(Index - 1));
|
|
case RemainingArgsClass: {
|
|
// Matches iff this is an exact match.
|
|
if (SpellingSize != ArgStringSize)
|
|
return nullptr;
|
|
auto A = std::make_unique<Arg>(*this, Spelling, Index++);
|
|
while (Index < Args.getNumInputArgStrings() &&
|
|
Args.getArgString(Index) != nullptr)
|
|
A->getValues().push_back(Args.getArgString(Index++));
|
|
return A;
|
|
}
|
|
case RemainingArgsJoinedClass: {
|
|
auto A = std::make_unique<Arg>(*this, Spelling, Index);
|
|
if (SpellingSize != ArgStringSize) {
|
|
// An inexact match means there is a joined arg.
|
|
A->getValues().push_back(Args.getArgString(Index) + SpellingSize);
|
|
}
|
|
Index++;
|
|
while (Index < Args.getNumInputArgStrings() &&
|
|
Args.getArgString(Index) != nullptr)
|
|
A->getValues().push_back(Args.getArgString(Index++));
|
|
return A;
|
|
}
|
|
|
|
default:
|
|
llvm_unreachable("Invalid option kind!");
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Arg> Option::accept(const ArgList &Args, StringRef CurArg,
|
|
bool GroupedShortOption,
|
|
unsigned &Index) const {
|
|
auto A(GroupedShortOption && getKind() == FlagClass
|
|
? std::make_unique<Arg>(*this, CurArg, Index)
|
|
: acceptInternal(Args, CurArg, Index));
|
|
if (!A)
|
|
return nullptr;
|
|
|
|
const Option &UnaliasedOption = getUnaliasedOption();
|
|
if (getID() == UnaliasedOption.getID())
|
|
return A;
|
|
|
|
// "A" is an alias for a different flag. For most clients it's more convenient
|
|
// if this function returns unaliased Args, so create an unaliased arg for
|
|
// returning.
|
|
|
|
// This creates a completely new Arg object for the unaliased Arg because
|
|
// the alias and the unaliased arg can have different Kinds and different
|
|
// Values (due to AliasArgs<>).
|
|
|
|
// Get the spelling from the unaliased option.
|
|
StringRef UnaliasedSpelling = Args.MakeArgString(
|
|
Twine(UnaliasedOption.getPrefix()) + Twine(UnaliasedOption.getName()));
|
|
|
|
// It's a bit weird that aliased and unaliased arg share one index, but
|
|
// the index is mostly use as a memory optimization in render().
|
|
// Due to this, ArgList::getArgString(A->getIndex()) will return the spelling
|
|
// of the aliased arg always, while A->getSpelling() returns either the
|
|
// unaliased or the aliased arg, depending on which Arg object it's called on.
|
|
auto UnaliasedA =
|
|
std::make_unique<Arg>(UnaliasedOption, UnaliasedSpelling, A->getIndex());
|
|
Arg *RawA = A.get();
|
|
UnaliasedA->setAlias(std::move(A));
|
|
|
|
if (getKind() != FlagClass) {
|
|
// Values are usually owned by the ArgList. The exception are
|
|
// CommaJoined flags, where the Arg owns the values. For aliased flags,
|
|
// make the unaliased Arg the owner of the values.
|
|
// FIXME: There aren't many uses of CommaJoined -- try removing
|
|
// CommaJoined in favor of just calling StringRef::split(',') instead.
|
|
UnaliasedA->getValues() = RawA->getValues();
|
|
UnaliasedA->setOwnsValues(RawA->getOwnsValues());
|
|
RawA->setOwnsValues(false);
|
|
return UnaliasedA;
|
|
}
|
|
|
|
// FlagClass aliases can have AliasArgs<>; add those to the unaliased arg.
|
|
if (const char *Val = getAliasArgs()) {
|
|
while (*Val != '\0') {
|
|
UnaliasedA->getValues().push_back(Val);
|
|
|
|
// Move past the '\0' to the next argument.
|
|
Val += strlen(Val) + 1;
|
|
}
|
|
}
|
|
if (UnaliasedOption.getKind() == JoinedClass && !getAliasArgs())
|
|
// A Flag alias for a Joined option must provide an argument.
|
|
UnaliasedA->getValues().push_back("");
|
|
return UnaliasedA;
|
|
}
|