Skip to content

Commit

Permalink
Rework the Option library to reduce dynamic relocations (llvm#119198)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chandlerc authored Dec 11, 2024
1 parent 151901c commit dd647e3
Show file tree
Hide file tree
Showing 52 changed files with 777 additions and 541 deletions.
20 changes: 4 additions & 16 deletions clang-tools-extra/clangd/CompileCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,20 +458,6 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
PrevAlias[Self] = T;
NextAlias[T] = Self;
};
// Also grab prefixes for each option, these are not fully exposed.
llvm::ArrayRef<llvm::StringLiteral> Prefixes[DriverID::LastOption];

#define PREFIX(NAME, VALUE) \
static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
METAVAR, VALUES) \
Prefixes[DriverID::OPT_##ID] = PREFIX;
#include "clang/Driver/Options.inc"
#undef OPTION
#undef PREFIX

struct {
DriverID ID;
Expand All @@ -498,7 +484,9 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
llvm::SmallVector<Rule> Rules;
// Iterate over each alias, to add rules for parsing it.
for (unsigned A = ID; A != DriverID::OPT_INVALID; A = NextAlias[A]) {
if (!Prefixes[A].size()) // option groups.
llvm::SmallVector<llvm::StringRef, 4> Prefixes;
DriverTable.appendOptionPrefixes(A, Prefixes);
if (Prefixes.empty()) // option groups.
continue;
auto Opt = DriverTable.getOption(A);
// Exclude - and -foo pseudo-options.
Expand All @@ -507,7 +495,7 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
auto Modes = getModes(Opt);
std::pair<unsigned, unsigned> ArgCount = getArgCount(Opt);
// Iterate over each spelling of the alias, e.g. -foo vs --foo.
for (StringRef Prefix : Prefixes[A]) {
for (StringRef Prefix : Prefixes) {
llvm::SmallString<64> Buf(Prefix);
Buf.append(Opt.getName());
llvm::StringRef Spelling = Result->try_emplace(Buf).first->getKey();
Expand Down
23 changes: 11 additions & 12 deletions clang/lib/Driver/DriverOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,21 @@ using namespace clang::driver;
using namespace clang::driver::options;
using namespace llvm::opt;

#define OPTTABLE_STR_TABLE_CODE
#include "clang/Driver/Options.inc"
#undef OPTTABLE_STR_TABLE_CODE

#define OPTTABLE_VALUES_CODE
#include "clang/Driver/Options.inc"
#undef OPTTABLE_VALUES_CODE

#define PREFIX(NAME, VALUE) \
static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTTABLE_PREFIXES_TABLE_CODE
#include "clang/Driver/Options.inc"
#undef PREFIX
#undef OPTTABLE_PREFIXES_TABLE_CODE

static constexpr const llvm::StringLiteral PrefixTable_init[] =
#define PREFIX_UNION(VALUES) VALUES
#define OPTTABLE_PREFIXES_UNION_CODE
#include "clang/Driver/Options.inc"
#undef PREFIX_UNION
;
static constexpr const llvm::ArrayRef<llvm::StringLiteral>
PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
#undef OPTTABLE_PREFIXES_UNION_CODE

static constexpr OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
Expand All @@ -43,7 +40,9 @@ namespace {

class DriverOptTable : public PrecomputedOptTable {
public:
DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {}
DriverOptTable()
: PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
OptionPrefixesUnion) {}
};
}

Expand Down
63 changes: 43 additions & 20 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,14 @@ CowCompilerInvocation::getMutPreprocessorOutputOpts() {

using ArgumentConsumer = CompilerInvocation::ArgumentConsumer;

#define OPTTABLE_STR_TABLE_CODE
#include "clang/Driver/Options.inc"
#undef OPTTABLE_STR_TABLE_CODE

static llvm::StringRef lookupStrInTable(unsigned Offset) {
return &OptionStrTable[Offset];
}

#define SIMPLE_ENUM_VALUE_TABLE
#include "clang/Driver/Options.inc"
#undef SIMPLE_ENUM_VALUE_TABLE
Expand All @@ -303,6 +311,11 @@ static std::optional<bool> normalizeSimpleNegativeFlag(OptSpecifier Opt,
/// denormalizeSimpleFlags never looks at it. Avoid bloating compile-time with
/// unnecessary template instantiations and just ignore it with a variadic
/// argument.
static void denormalizeSimpleFlag(ArgumentConsumer Consumer,
unsigned SpellingOffset, Option::OptionClass,
unsigned, /*T*/...) {
Consumer(lookupStrInTable(SpellingOffset));
}
static void denormalizeSimpleFlag(ArgumentConsumer Consumer,
const Twine &Spelling, Option::OptionClass,
unsigned, /*T*/...) {
Expand Down Expand Up @@ -343,10 +356,10 @@ static auto makeBooleanOptionNormalizer(bool Value, bool OtherValue,
}

static auto makeBooleanOptionDenormalizer(bool Value) {
return [Value](ArgumentConsumer Consumer, const Twine &Spelling,
return [Value](ArgumentConsumer Consumer, unsigned SpellingOffset,
Option::OptionClass, unsigned, bool KeyPath) {
if (KeyPath == Value)
Consumer(Spelling);
Consumer(lookupStrInTable(SpellingOffset));
};
}

Expand All @@ -371,6 +384,14 @@ static void denormalizeStringImpl(ArgumentConsumer Consumer,
}
}

template <typename T>
static void
denormalizeString(ArgumentConsumer Consumer, unsigned SpellingOffset,
Option::OptionClass OptClass, unsigned TableIndex, T Value) {
denormalizeStringImpl(Consumer, lookupStrInTable(SpellingOffset), OptClass,
TableIndex, Twine(Value));
}

template <typename T>
static void denormalizeString(ArgumentConsumer Consumer, const Twine &Spelling,
Option::OptionClass OptClass, unsigned TableIndex,
Expand Down Expand Up @@ -417,14 +438,14 @@ static std::optional<unsigned> normalizeSimpleEnum(OptSpecifier Opt,
}

static void denormalizeSimpleEnumImpl(ArgumentConsumer Consumer,
const Twine &Spelling,
unsigned SpellingOffset,
Option::OptionClass OptClass,
unsigned TableIndex, unsigned Value) {
assert(TableIndex < SimpleEnumValueTablesSize);
const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex];
if (auto MaybeEnumVal = findValueTableByValue(Table, Value)) {
denormalizeString(Consumer, Spelling, OptClass, TableIndex,
MaybeEnumVal->Name);
denormalizeString(Consumer, lookupStrInTable(SpellingOffset), OptClass,
TableIndex, MaybeEnumVal->Name);
} else {
llvm_unreachable("The simple enum value was not correctly defined in "
"the tablegen option description");
Expand All @@ -433,11 +454,11 @@ static void denormalizeSimpleEnumImpl(ArgumentConsumer Consumer,

template <typename T>
static void denormalizeSimpleEnum(ArgumentConsumer Consumer,
const Twine &Spelling,
unsigned SpellingOffset,
Option::OptionClass OptClass,
unsigned TableIndex, T Value) {
return denormalizeSimpleEnumImpl(Consumer, Spelling, OptClass, TableIndex,
static_cast<unsigned>(Value));
return denormalizeSimpleEnumImpl(Consumer, SpellingOffset, OptClass,
TableIndex, static_cast<unsigned>(Value));
}

static std::optional<std::string> normalizeString(OptSpecifier Opt,
Expand Down Expand Up @@ -473,7 +494,7 @@ normalizeStringVector(OptSpecifier Opt, int, const ArgList &Args,
}

static void denormalizeStringVector(ArgumentConsumer Consumer,
const Twine &Spelling,
unsigned SpellingOffset,
Option::OptionClass OptClass,
unsigned TableIndex,
const std::vector<std::string> &Values) {
Expand All @@ -487,15 +508,16 @@ static void denormalizeStringVector(ArgumentConsumer Consumer,
CommaJoinedValue.append(Value);
}
}
denormalizeString(Consumer, Spelling, Option::OptionClass::JoinedClass,
TableIndex, CommaJoinedValue);
denormalizeString(Consumer, SpellingOffset,
Option::OptionClass::JoinedClass, TableIndex,
CommaJoinedValue);
break;
}
case Option::JoinedClass:
case Option::SeparateClass:
case Option::JoinedOrSeparateClass:
for (const std::string &Value : Values)
denormalizeString(Consumer, Spelling, OptClass, TableIndex, Value);
denormalizeString(Consumer, SpellingOffset, OptClass, TableIndex, Value);
break;
default:
llvm_unreachable("Cannot denormalize an option with option class "
Expand Down Expand Up @@ -532,10 +554,11 @@ static T extractMaskValue(T KeyPath) {
}

#define PARSE_OPTION_WITH_MARSHALLING( \
ARGS, DIAGS, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
ARGS, DIAGS, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS, \
ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
METAVAR, VALUES, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, \
TABLE_INDEX) \
if ((VISIBILITY) & options::CC1Option) { \
KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE); \
if (IMPLIED_CHECK) \
Expand All @@ -549,8 +572,8 @@ static T extractMaskValue(T KeyPath) {
// Capture the extracted value as a lambda argument to avoid potential issues
// with lifetime extension of the reference.
#define GENERATE_OPTION_WITH_MARSHALLING( \
CONSUMER, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
CONSUMER, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
if ((VISIBILITY) & options::CC1Option) { \
Expand All @@ -559,8 +582,8 @@ static T extractMaskValue(T KeyPath) {
(Extracted != \
static_cast<decltype(KEYPATH)>((IMPLIED_CHECK) ? (IMPLIED_VALUE) \
: (DEFAULT_VALUE)))) \
DENORMALIZER(CONSUMER, SPELLING, Option::KIND##Class, TABLE_INDEX, \
Extracted); \
DENORMALIZER(CONSUMER, SPELLING_OFFSET, Option::KIND##Class, \
TABLE_INDEX, Extracted); \
}(EXTRACTOR(KEYPATH)); \
}

Expand Down
42 changes: 12 additions & 30 deletions clang/tools/clang-installapi/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,21 @@ namespace drv = clang::driver::options;
namespace clang {
namespace installapi {

/// Create prefix string literals used in InstallAPIOpts.td.
#define PREFIX(NAME, VALUE) \
static constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTTABLE_STR_TABLE_CODE
#include "InstallAPIOpts.inc"
#undef PREFIX
#undef OPTTABLE_STR_TABLE_CODE

static constexpr const llvm::StringLiteral PrefixTable_init[] =
#define PREFIX_UNION(VALUES) VALUES
#define OPTTABLE_PREFIXES_TABLE_CODE
#include "InstallAPIOpts.inc"
#undef PREFIX_UNION
;
static constexpr const ArrayRef<StringLiteral>
PrefixTable(PrefixTable_init, std::size(PrefixTable_init) - 1);
#undef OPTTABLE_PREFIXES_TABLE_CODE

#define OPTTABLE_PREFIXES_UNION_CODE
#include "InstallAPIOpts.inc"
#undef OPTTABLE_PREFIXES_UNION_CODE

/// Create table mapping all options defined in InstallAPIOpts.td.
static constexpr OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
VALUES) \
{PREFIX, \
NAME, \
HELPTEXT, \
HELPTEXTSFORVARIANTS, \
METAVAR, \
OPT_##ID, \
Option::KIND##Class, \
PARAM, \
FLAGS, \
VISIBILITY, \
OPT_##GROUP, \
OPT_##ALIAS, \
ALIASARGS, \
VALUES},
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
#include "InstallAPIOpts.inc"
#undef OPTION
};
Expand All @@ -75,7 +55,9 @@ namespace {
/// \brief Create OptTable class for parsing actual command line arguments.
class DriverOptTable : public opt::PrecomputedOptTable {
public:
DriverOptTable() : PrecomputedOptTable(InfoTable, PrefixTable) {}
DriverOptTable()
: PrecomputedOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
OptionPrefixesUnion) {}
};

} // end anonymous namespace.
Expand Down
14 changes: 8 additions & 6 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,13 @@ enum ID {
#undef OPTION
};

#define PREFIX(NAME, VALUE) \
static constexpr StringLiteral NAME##_init[] = VALUE; \
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
std::size(NAME##_init) - 1);
#define OPTTABLE_STR_TABLE_CODE
#include "LinkerWrapperOpts.inc"
#undef PREFIX
#undef OPTTABLE_STR_TABLE_CODE

#define OPTTABLE_PREFIXES_TABLE_CODE
#include "LinkerWrapperOpts.inc"
#undef OPTTABLE_PREFIXES_TABLE_CODE

static constexpr OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
Expand All @@ -189,7 +190,8 @@ static constexpr OptTable::Info InfoTable[] = {

class WrapperOptTable : public opt::GenericOptTable {
public:
WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
WrapperOptTable()
: opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
};

const OptTable &getOptTable() {
Expand Down
14 changes: 8 additions & 6 deletions clang/tools/clang-nvlink-wrapper/ClangNVLinkWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,13 @@ enum ID {
#undef OPTION
};

#define PREFIX(NAME, VALUE) \
static constexpr StringLiteral NAME##_init[] = VALUE; \
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
std::size(NAME##_init) - 1);
#define OPTTABLE_STR_TABLE_CODE
#include "NVLinkOpts.inc"
#undef PREFIX
#undef OPTTABLE_STR_TABLE_CODE

#define OPTTABLE_PREFIXES_TABLE_CODE
#include "NVLinkOpts.inc"
#undef OPTTABLE_PREFIXES_TABLE_CODE

static constexpr OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
Expand All @@ -124,7 +125,8 @@ static constexpr OptTable::Info InfoTable[] = {

class WrapperOptTable : public opt::GenericOptTable {
public:
WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
WrapperOptTable()
: opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
};

const OptTable &getOptTable() {
Expand Down
14 changes: 8 additions & 6 deletions clang/tools/clang-scan-deps/ClangScanDeps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ enum ID {
#undef OPTION
};

#define PREFIX(NAME, VALUE) \
constexpr llvm::StringLiteral NAME##_init[] = VALUE; \
constexpr llvm::ArrayRef<llvm::StringLiteral> NAME( \
NAME##_init, std::size(NAME##_init) - 1);
#define OPTTABLE_STR_TABLE_CODE
#include "Opts.inc"
#undef PREFIX
#undef OPTTABLE_STR_TABLE_CODE

#define OPTTABLE_PREFIXES_TABLE_CODE
#include "Opts.inc"
#undef OPTTABLE_PREFIXES_TABLE_CODE

const llvm::opt::OptTable::Info InfoTable[] = {
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
Expand All @@ -65,7 +66,8 @@ const llvm::opt::OptTable::Info InfoTable[] = {

class ScanDepsOptTable : public llvm::opt::GenericOptTable {
public:
ScanDepsOptTable() : GenericOptTable(InfoTable) {
ScanDepsOptTable()
: GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {
setGroupedShortOptions(true);
}
};
Expand Down
Loading

0 comments on commit dd647e3

Please sign in to comment.