1
1
mirror of https://github.com/rui314/mold.git synced 2024-11-13 09:39:13 +03:00

[ELF] Make version script application faster

Instead of calling regex match multiple times, create a single
regex and call regex match function only once.

https://github.com/rui314/mold/issues/156
This commit is contained in:
Rui Ueyama 2021-12-17 18:42:35 +09:00
parent 36633895e5
commit 455e729393
4 changed files with 74 additions and 32 deletions

View File

@ -255,9 +255,9 @@ static bool read_label(std::span<std::string_view> &tok,
}
template <typename E>
static void read_version_script_commands(Context<E> &ctx,
std::span<std::string_view> &tok,
i16 &ver, bool is_extern_cpp) {
static void
read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
VersionPattern &pat, bool is_extern_cpp) {
bool is_global = true;
while (!tok.empty() && tok[0] != "}") {
@ -284,16 +284,19 @@ static void read_version_script_commands(Context<E> &ctx,
}
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver, is_cpp);
read_version_script_commands(ctx, tok, pat, is_cpp);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
continue;
}
if (tok[0] == "*")
ctx.arg.default_version = (is_global ? ver : VER_NDX_LOCAL);
ctx.arg.default_version = (is_global ? pat.ver_idx : VER_NDX_LOCAL);
else if (is_extern_cpp)
pat.cpp_patterns.push_back(tok[0]);
else
ctx.arg.version_patterns.push_back({tok[0], ver, is_extern_cpp});
pat.patterns.push_back(tok[0]);
tok = tok.subspan(1);
if (!tok.empty() && tok[0] == "}")
@ -307,19 +310,24 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
i16 next_ver = VER_NDX_LAST_RESERVED + 1;
while (!tok.empty() && tok[0] != "}") {
i16 ver = VER_NDX_GLOBAL;
if (tok[0] != "{") {
ver = next_ver++;
VersionPattern pat;
if (tok[0] == "{") {
pat.ver_idx = VER_NDX_GLOBAL;
} else {
pat.ver_idx = next_ver++;
ctx.arg.version_definitions.push_back(tok[0]);
tok = tok.subspan(1);
}
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver, false);
read_version_script_commands(ctx, tok, pat, false);
tok = skip(ctx, tok, "}");
if (!tok.empty() && tok[0] != ";")
tok = tok.subspan(1);
tok = skip(ctx, tok, ";");
ctx.arg.version_patterns.push_back(pat);
}
}
@ -356,10 +364,15 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
continue;
}
if (tok[0] == "*")
if (tok[0] == "*") {
ctx.arg.default_version = ver;
else
ctx.arg.version_patterns.push_back({tok[0], ver, false});
} else {
VersionPattern pat;
pat.ver_idx = ver;
pat.patterns.push_back(tok[0]);
ctx.arg.version_patterns.push_back(pat);
}
tok = skip(ctx, tok.subspan(1), ";");
}

View File

@ -16,7 +16,7 @@
namespace mold::elf {
std::regex glob_to_regex(std::string_view pattern) {
std::string glob_to_regex(std::string_view pattern) {
std::stringstream ss;
for (u8 c : pattern) {
if (c == '*')
@ -24,7 +24,7 @@ std::regex glob_to_regex(std::string_view pattern) {
else
ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)c;
}
return std::regex(ss.str(), std::regex::optimize);
return ss.str();
}
template <typename E>

View File

@ -1156,9 +1156,9 @@ typedef enum { COMPRESS_NONE, COMPRESS_GABI, COMPRESS_GNU } CompressKind;
typedef enum { ERROR, WARN, IGNORE } UnresolvedKind;
struct VersionPattern {
std::string_view pattern;
i16 ver_idx;
bool is_extern_cpp;
std::vector<std::string_view> patterns;
std::vector<std::string_view> cpp_patterns;
};
template <typename E, typename T>
@ -1403,7 +1403,7 @@ MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string path);
template <typename E>
void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf);
std::regex glob_to_regex(std::string_view pat);
std::string glob_to_regex(std::string_view pat);
int main(int argc, char **argv);

View File

@ -589,26 +589,55 @@ template <typename E>
void apply_version_script(Context<E> &ctx) {
Timer t(ctx, "apply_version_script");
for (VersionPattern &elem : ctx.arg.version_patterns) {
assert(elem.pattern != "*");
auto to_regex = [](std::span<std::string_view> vec) -> std::string {
switch (vec.size()) {
case 0:
return "";
case 1:
return glob_to_regex(vec[0]);
default:
std::string re = glob_to_regex(vec[0]);
for (std::string_view s : vec.subspan(1))
re += "|" + glob_to_regex(s);
return re;
}
};
if (!elem.is_extern_cpp &&
elem.pattern.find('*') == elem.pattern.npos) {
Symbol<E> *sym = intern(ctx, elem.pattern);
if (sym->file && !sym->file->is_dso)
sym->ver_idx = elem.ver_idx;
continue;
for (VersionPattern &elem : ctx.arg.version_patterns) {
std::vector<std::string_view> vec;
for (std::string_view pat : elem.patterns) {
if (pat.find('*') == pat.npos) {
Symbol<E> *sym = intern(ctx, pat);
if (sym->file && !sym->file->is_dso)
sym->ver_idx = elem.ver_idx;
} else {
vec.push_back(pat);
}
}
std::regex re = glob_to_regex(elem.pattern);
if (vec.empty() && elem.cpp_patterns.empty())
continue;
auto flags = std::regex_constants::optimize | std::regex_constants::nosubs;
std::regex re(to_regex(vec), flags);
std::regex cpp_re(to_regex(elem.cpp_patterns), flags);
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file == file) {
std::string_view name = sym->name();
if (elem.is_extern_cpp)
name = demangle(name);
if (std::regex_match(name.begin(), name.end(), re))
if (sym->file != file)
continue;
std::string_view name = sym->name();
if (!vec.empty() && std::regex_match(name.begin(), name.end(), re)) {
sym->ver_idx = elem.ver_idx;
continue;
}
if (!elem.cpp_patterns.empty()) {
std::string_view s = demangle(name);
if (std::regex_match(s.begin(), s.end(), cpp_re))
sym->ver_idx = elem.ver_idx;
}
}