diff --git a/bin/Index/ArgumentFilter.cpp b/bin/Index/ArgumentFilter.cpp new file mode 100644 index 000000000..22aec2394 --- /dev/null +++ b/bin/Index/ArgumentFilter.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2022-present, Trail of Bits, Inc. +// +// This source code is licensed in accordance with the terms specified in +// the LICENSE file found in the root directory of this source tree. + +#include "ArgumentFilter.h" + +#include +#include +#include + +namespace indexer { + +std::optional +ArgumentFilter::LoadFromFile(const std::filesystem::path &path) { + std::ifstream file(path); + if (!file.is_open()) { + return "Could not open argument filter config: " + path.string(); + } + + std::string line; + unsigned line_num = 0; + + while (std::getline(file, line)) { + ++line_num; + + // Strip leading whitespace. + auto start = line.find_first_not_of(" \t"); + if (start == std::string::npos) { + continue; // Empty line. + } + + // Skip comments. + if (line[start] == '#') { + continue; + } + + // Parse: [] + std::istringstream ss(line.substr(start)); + std::string match_str; + std::string pattern; + std::string num_str; + + ss >> match_str >> pattern; + if (match_str.empty() || pattern.empty()) { + return path.string() + ":" + std::to_string(line_num) + + ": expected ' []'"; + } + + MatchType match; + if (match_str == "exact") { + match = MatchType::kExact; + } else if (match_str == "prefix") { + match = MatchType::kPrefix; + } else if (match_str == "contains") { + match = MatchType::kContains; + } else { + return path.string() + ":" + std::to_string(line_num) + + ": unknown match type '" + match_str + + "'; expected 'exact', 'prefix', or 'contains'"; + } + + int num_following = 0; + ss >> num_str; + if (!num_str.empty()) { + bool lenient = false; + std::string_view digits = num_str; + + // ~N means lenient: skip following args only if they don't look like + // flags (don't start with '-'). + if (digits.front() == '~') { + lenient = true; + digits.remove_prefix(1); + } + + auto [ptr, ec] = std::from_chars( + digits.data(), digits.data() + digits.size(), num_following); + if (ec != std::errc{} || ptr != digits.data() + digits.size()) { + return path.string() + ":" + std::to_string(line_num) + + ": invalid num_values '" + num_str + "'"; + } + + if (lenient) { + num_following = -num_following; + } + } + + rules.push_back({match, std::move(pattern), num_following}); + } + + return std::nullopt; +} + +std::optional +ArgumentFilter::ShouldSkip(std::string_view arg) const { + for (const Rule &rule : rules) { + bool matched = false; + switch (rule.match) { + case MatchType::kExact: + matched = (arg == rule.pattern); + break; + case MatchType::kPrefix: + matched = arg.starts_with(rule.pattern); + break; + case MatchType::kContains: + matched = (arg.find(rule.pattern) != std::string_view::npos); + break; + } + if (matched) { + return rule.num_following; + } + } + return std::nullopt; +} + +} // namespace indexer diff --git a/bin/Index/ArgumentFilter.h b/bin/Index/ArgumentFilter.h new file mode 100644 index 000000000..492c91fc5 --- /dev/null +++ b/bin/Index/ArgumentFilter.h @@ -0,0 +1,63 @@ +// Copyright (c) 2022-present, Trail of Bits, Inc. +// +// This source code is licensed in accordance with the terms specified in +// the LICENSE file found in the root directory of this source tree. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace indexer { + +// Filters compiler arguments based on pattern rules loaded from a config file. +// +// Each rule specifies a match type (exact, prefix, or contains), a pattern +// string, and how many following arguments to also skip. Rules are checked in +// order; the first match wins. +class ArgumentFilter { + public: + ArgumentFilter(void) = default; + + // Load rules from a config file, appending them to any existing rules. + // Returns an error message on failure, or std::nullopt on success. + std::optional LoadFromFile(const std::filesystem::path &path); + + // Check if an argument should be skipped. Returns the number of following + // arguments to also skip (0 means skip only this argument), or std::nullopt + // if the argument should be kept. + // + // When the returned value is negative, its absolute value is the number of + // following arguments to skip, but only if they don't start with '-'. This + // handles malformed compilation databases where a "value" slot actually + // contains the next flag. + std::optional ShouldSkip(std::string_view arg) const; + + // Returns true if no rules have been loaded. + bool Empty(void) const { return rules.empty(); } + + private: + enum class MatchType : uint8_t { + kExact, + kPrefix, + kContains, + }; + + struct Rule { + MatchType match; + std::string pattern; + + // Positive: unconditionally skip this many following args. + // Negative: skip |n| following args only if they don't start with '-'. + // Zero: skip only the matched argument itself. + int num_following; + }; + + std::vector rules; +}; + +} // namespace indexer diff --git a/bin/Index/CMakeLists.txt b/bin/Index/CMakeLists.txt index 83ed3048b..2c0e9367c 100644 --- a/bin/Index/CMakeLists.txt +++ b/bin/Index/CMakeLists.txt @@ -10,6 +10,8 @@ set(exe_name "mx-index") add_executable("${exe_name}" "Action.cpp" "Action.h" + "ArgumentFilter.cpp" + "ArgumentFilter.h" "BuildPendingFragment.cpp" "Context.cpp" "Context.h" @@ -96,6 +98,11 @@ target_include_directories("${exe_name}" "$" ) +target_compile_definitions("${exe_name}" + PRIVATE + "MX_SHARE_DIR=\"${MX_INSTALL_SHARE_DIR}\"" +) + target_compile_options("${exe_name}" PRIVATE "-Wno-unknown-warning-option" @@ -122,6 +129,27 @@ set_target_properties("${exe_name}" find_and_link_llvm_dependencies("${exe_name}") +# Copy the default argument filter config into the build tree so that mx-index +# can find it when run from the build directory (the binary sits in +# //, and it looks for ..//multiplier/). +set(MX_ARG_FILTER_SRC "${PROJECT_SOURCE_DIR}/share/multiplier/unsupported_args.cfg") +set(MX_ARG_FILTER_DST "${PROJECT_BINARY_DIR}/${MX_INSTALL_SHARE_DIR}/multiplier/unsupported_args.cfg") + +# Skip the copy when source and build trees overlap (avoids a dependency cycle). +cmake_path(ABSOLUTE_PATH MX_ARG_FILTER_SRC NORMALIZE OUTPUT_VARIABLE _src_abs) +cmake_path(ABSOLUTE_PATH MX_ARG_FILTER_DST NORMALIZE OUTPUT_VARIABLE _dst_abs) +if(NOT _src_abs STREQUAL _dst_abs) + add_custom_command( + OUTPUT "${MX_ARG_FILTER_DST}" + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${MX_ARG_FILTER_SRC}" "${MX_ARG_FILTER_DST}" + DEPENDS "${MX_ARG_FILTER_SRC}" + COMMENT "Copying unsupported_args.cfg to build tree" + ) + add_custom_target("${exe_name}-arg-filter" ALL DEPENDS "${MX_ARG_FILTER_DST}") + add_dependencies("${exe_name}" "${exe_name}-arg-filter") +endif() + if(MX_ENABLE_INSTALL AND NOT MX_ENABLE_BOOTSTRAP) install( TARGETS @@ -132,4 +160,11 @@ if(MX_ENABLE_INSTALL AND NOT MX_ENABLE_BOOTSTRAP) DESTINATION "${CMAKE_INSTALL_BINDIR}" ) + + install( + FILES + "${MX_ARG_FILTER_SRC}" + DESTINATION + "${MX_INSTALL_SHARE_DIR}/multiplier" + ) endif() diff --git a/bin/Index/Hash.cpp b/bin/Index/Hash.cpp index 8f4ee977d..cd936e0fa 100644 --- a/bin/Index/Hash.cpp +++ b/bin/Index/Hash.cpp @@ -379,8 +379,14 @@ void HashVisitor::VisitDecl(const pasta::Decl &decl) { AccumulateTokenData(ss, pasta::PrintedTokenRange::Create(vd->Type(), pp)); } else if (auto td = pasta::TypeDecl::From(decl)) { - if (auto ty = td->TypeForDeclaration()) { - AccumulateTokenData(ss, pasta::PrintedTokenRange::Create(ty.value(), pp)); + // Skip TypeForDeclaration() for TypedefNameDecls: the typedef body tokens + // are already in the fragment's token range, and the type printer can + // produce different output across TUs depending on how much type sugar + // Clang preserved, causing hash instability. + if (!pasta::TypedefNameDecl::From(decl)) { + if (auto ty = td->TypeForDeclaration()) { + AccumulateTokenData(ss, pasta::PrintedTokenRange::Create(ty.value(), pp)); + } } } @@ -403,10 +409,6 @@ static std::string HashNestedFragment( HashVisitor visitor(ss, em, true); visitor.Accept(decl); - // std::cerr << "\n-----------------\n"; - // std::cerr << ss.str() << '\n'; - // Dump(decl); - return ss.str(); } diff --git a/bin/Index/Importer.cpp b/bin/Index/Importer.cpp index 25fdbe5a0..7a4feff6d 100644 --- a/bin/Index/Importer.cpp +++ b/bin/Index/Importer.cpp @@ -46,6 +46,7 @@ #pragma clang diagnostic pop #include "Action.h" +#include "ArgumentFilter.h" #include "Context.h" #include "Executor.h" #include "IndexCompileJob.h" @@ -208,6 +209,7 @@ class BuildCommandAction final : public Action { std::shared_ptr fs; const Command &command; GlobalIndexingState &ctx; + const ArgumentFilter &arg_filter; void RunWithCompiler(pasta::CompileCommand cmd, pasta::Compiler cc); @@ -219,11 +221,13 @@ class BuildCommandAction final : public Action { virtual ~BuildCommandAction(void) = default; inline BuildCommandAction(pasta::FileManager &fm_, const Command &command_, - GlobalIndexingState &ctx_) + GlobalIndexingState &ctx_, + const ArgumentFilter &arg_filter_) : fm(fm_), fs(fm.FileSystem()), command(command_), - ctx(ctx_) {} + ctx(ctx_), + arg_filter(arg_filter_) {} void Run(void) final; }; @@ -233,13 +237,16 @@ class BuildCommandAction final : public Action { std::variant BuildCommandAction::GetCompilerInfo(void) { std::vector new_args; - bool skip = false; - bool skip_internal_option = false; bool has_output = false; bool next_is_language = false; std::string_view inferred_lang = "c"; std::string_view specified_lang = "c"; bool specifies_language = false; + + // Number of following arguments to skip. Positive means unconditional; + // negative means skip only if the argument doesn't look like a flag. + int skip_following = 0; + for (const char *arg_ : command.vec) { std::string_view arg(arg_); @@ -258,16 +265,22 @@ BuildCommandAction::GetCompilerInfo(void) { continue; } - if (skip) { - skip = false; + // Handle skipping of following arguments from a previous match. + if (skip_following > 0) { + --skip_following; + continue; + } else if (skip_following < 0) { + ++skip_following; // NOTE(pag): Have observed things like the following in the Linux kernel: // // ... -main-file-name -mrelocation-model ... // + // In lenient mode, only skip if the argument doesn't look like a flag. if (arg.front() != '-' && 1u < arg.size()) { continue; } + // Looks like a flag; fall through to process it normally. } // Try to detect C++ code. @@ -298,7 +311,7 @@ BuildCommandAction::GetCompilerInfo(void) { } else if (arg_str.ends_with(".s")) { inferred_lang = "asm"; - + } else if (arg_str.ends_with(".ll") || arg_str.ends_with(".ir")) { inferred_lang = "ir"; @@ -308,71 +321,16 @@ BuildCommandAction::GetCompilerInfo(void) { } } - if (skip_internal_option) { - skip_internal_option = false; + // Check the argument filter for patterns to elide. + if (auto n = arg_filter.ShouldSkip(arg)) { + skip_following = *n; continue; } - // Drop things like `-Wall`, `-Werror, `-fsanitize=..`, etc. - if (arg.starts_with("-W") || - arg.starts_with("-pedantic") || - arg.starts_with("-ftrivial-auto-var-init=") || - arg.starts_with("-fpatchable-function-entry=") || - arg.starts_with("-fpatchable-function-entry-offset=") || - arg.starts_with("-fstrict-flex-arrays=") || - arg.starts_with("-mfunction-return=") || - arg.starts_with("-fsanitize=") || - arg.starts_with("-fcoverage-compilation-dir=") || - arg.starts_with("-fcrash-diagnostics-dir=") || - arg == "-fskip-odr-check-in-gmf" || - arg == "-pic-is-pie" || - arg == "-mindirect-branch-cs-prefix" || - arg == "-Wno-cast-function-type-strict" || - arg == "-Wno-c++11-narrowing-const-reference" || - arg == "-Wno-thread-safety-reference-return") { - continue; // Skip the argument. - - // Keep the argument. - } else if (arg.starts_with("-Wno-")) { - - // Drop these, and the following argument. - } else if (arg == "-Xclang" || arg == "-mllvm") { - skip_internal_option = true; - continue; - - // Drop these, and the following argument. - } else if (arg == "-dependency-file" || - arg == "-diagnostic-log-file" || - arg == "-header-include-file" || - arg == "-stack-usage-file" || - arg == "-mrelocation-model" || - arg == "-pic-level" || - arg == "-main-file-name" || - arg == "-MT" || - arg == "-MQ") { - skip = true; - continue; - - // If it specifies some file, e.g. `-frandomize-layout-seed-file=...` or - // `-fprofile-remapping-file=`, or ..., then drop it. - } else if (strstr(arg_, "-file=") /* NOTE(pag): find anywhere */ || - arg.starts_with("-dependent-lib=") || - arg.starts_with("-stats-file=") || - arg.starts_with("-fprofile-list=") || - arg.starts_with("-fxray-always-instrument=") || - arg.starts_with("-fxray-never-instrument=") || - arg.starts_with("-fxray-attr-list=") || - arg.starts_with("-tsan-compound-read-before-write=") || - arg.starts_with("-tsan-distinguish-volatile=") || - arg.starts_with("-treat") || - arg.starts_with("-split-threshold-for-reg-with-hint=") || - arg.starts_with("-instcombine-lower-dbg-declare=")) { - continue; - // Output file, `-o `, `--output `. - } else if (arg == "-o" || arg == "--output") { + if (arg == "-o" || arg == "--output") { has_output = true; - skip = true; + skip_following = 1; continue; // `--output=` @@ -410,8 +368,9 @@ BuildCommandAction::GetCompilerInfo(void) { continue; // Something like `"-DFOO=bar"` or `'-DFOO=bar'`. - } else if ((arg.front() == '\'' || arg.front() == '"') && arg[1] == '-' && - arg.back() == arg.front()) { + } else if (arg.size() >= 3 && + (arg.front() == '\'' || arg.front() == '"') && + arg[1] == '-' && arg.back() == arg.front()) { continue; } @@ -730,22 +689,26 @@ struct Importer::PrivateData { std::filesystem::path cwd; pasta::FileManager fm; GlobalIndexingState &ctx; + const ArgumentFilter &arg_filter; inline PrivateData(std::filesystem::path cwd_, const pasta::FileManager &fm_, - GlobalIndexingState &ctx_) + GlobalIndexingState &ctx_, + const ArgumentFilter &arg_filter_) : cwd(std::move(cwd_)), fm(fm_), - ctx(ctx_) {} + ctx(ctx_), + arg_filter(arg_filter_) {} }; Importer::~Importer(void) {} Importer::Importer(std::filesystem::path cwd_, const pasta::FileManager &fm, - GlobalIndexingState &ctx) + GlobalIndexingState &ctx, + const ArgumentFilter &arg_filter) : d(std::make_unique( - std::move(cwd_), fm, ctx)) {} + std::move(cwd_), fm, ctx, arg_filter)) {} bool Importer::ImportBlightCompileCommand(llvm::json::Object &o) { ProgressBarWork progress_tracker(d->ctx.command_progress); @@ -986,7 +949,8 @@ void Importer::Import(const ExecutorOptions &options) { } for (const Command &cmd : commands) { - per_path_exe.EmplaceAction(d->fm, cmd, d->ctx); + per_path_exe.EmplaceAction(d->fm, cmd, d->ctx, + d->arg_filter); } per_path_exe.Start(); diff --git a/bin/Index/Importer.h b/bin/Index/Importer.h index b998eb772..8f6228fc6 100644 --- a/bin/Index/Importer.h +++ b/bin/Index/Importer.h @@ -18,6 +18,7 @@ class FileManager; } namespace indexer { +class ArgumentFilter; class GlobalIndexingState; class EnvVariableMap : public std::unordered_map {}; struct ExecutorOptions; @@ -35,7 +36,8 @@ class Importer { explicit Importer(std::filesystem::path cwd_, const pasta::FileManager &fm, - GlobalIndexingState &context); + GlobalIndexingState &context, + const ArgumentFilter &arg_filter); bool ImportBlightCompileCommand(llvm::json::Object &o); bool ImportCMakeCompileCommand(llvm::json::Object &o, diff --git a/bin/Index/Main.cpp b/bin/Index/Main.cpp index 64f04198f..2bb494c3e 100644 --- a/bin/Index/Main.cpp +++ b/bin/Index/Main.cpp @@ -28,12 +28,13 @@ #pragma clang diagnostic ignored "-Wshadow" #pragma clang diagnostic ignored "-Wcast-align" #include -//#include +#include #include #include #include #pragma clang diagnostic pop +#include "ArgumentFilter.h" #include "Context.h" #include "IdStore.h" #include "Importer.h" @@ -73,8 +74,16 @@ DEFINE_string(workspace, "mx-workspace", DEFINE_bool(fork_mode, false, "Use --fork_mode if running inside docker"); DEFINE_bool(reproc_mode, false, "Use --reproc_mode to use reproc library"); +DEFINE_string(extra_arg_patterns, "", + "Path to an additional argument filter config file. Patterns in " + "this file augment the default unsupported_args.cfg that ships " + "with mx-index. See that file for the format."); + namespace { +// Used as an address anchor for llvm::sys::fs::getMainExecutable. +static int gExeAnchor = 0; + std::unique_ptr ReadFileBuffer(const std::string file_name) { if (!file_name.empty()) { @@ -165,6 +174,7 @@ int main(int argc, char *argv[], char *envp[]) { << " [--num_indexer_workers N]\n" << " [--num_command_workers N]\n" << " [--env PATH_TO_COPIED_ENV_VARS]\n" + << " [--extra_arg_patterns PATH_TO_EXTRA_PATTERNS]\n" << " [--show_progress]\n" << " --fork_mode\n" << " --reproc_mode\n" @@ -280,8 +290,47 @@ int main(int argc, char *argv[], char *envp[]) { return EXIT_FAILURE; } + // Load the argument filter from the default config file that ships alongside + // the mx-index binary, then optionally augment with a user-provided file. + indexer::ArgumentFilter arg_filter; + + { + // Locate the default config relative to the executable: + // /bin/mx-index -> //multiplier/unsupported_args.cfg + auto exe_path = llvm::sys::fs::getMainExecutable( + argv[0], &gExeAnchor); + if (!exe_path.empty()) { + auto default_cfg = std::filesystem::path(exe_path).parent_path() + / ".." / MX_SHARE_DIR / "multiplier" + / "unsupported_args.cfg"; + std::error_code ec; + default_cfg = std::filesystem::canonical(default_cfg, ec); + if (!ec) { + if (auto err = arg_filter.LoadFromFile(default_cfg)) { + LOG(WARNING) << *err; + } + } else { + LOG(WARNING) + << "Could not find default argument filter config at " + << default_cfg.string() << ": " << ec.message(); + } + } + + if (!FLAGS_extra_arg_patterns.empty()) { + if (auto err = arg_filter.LoadFromFile(FLAGS_extra_arg_patterns)) { + std::cerr << *err << "\n"; + return EXIT_FAILURE; + } + } + + if (arg_filter.Empty()) { + LOG(WARNING) << "No argument filter patterns loaded; unsupported " + "compiler arguments will not be stripped"; + } + } + llvm::LLVMContext llvm_context; - indexer::Importer importer(path.parent_path(), fm, context); + indexer::Importer importer(path.parent_path(), fm, context, arg_filter); // Parse the target, be it a compile commands JSON database or a binary // with embedded commands. diff --git a/share/multiplier/unsupported_args.cfg b/share/multiplier/unsupported_args.cfg new file mode 100644 index 000000000..1afa9be9a --- /dev/null +++ b/share/multiplier/unsupported_args.cfg @@ -0,0 +1,298 @@ +# Compiler argument patterns for mx-index to elide from compilation commands. +# +# When mx-index re-invokes the original compiler to discover system include +# paths and version information, arguments matching these patterns are stripped +# from the command line. This prevents the compiler invocation from failing due +# to unsupported or irrelevant flags. +# +# Format: +# +# [] +# +# match: +# exact - argument must equal exactly +# prefix - argument must start with +# contains - argument must contain as a substring +# +# num_values (how many subsequent arguments to also skip): +# (omitted or 0) - only skip the matched argument itself +# N - also skip the next N arguments unconditionally +# ~N - also skip the next N arguments, but only if they don't +# start with '-'. This handles malformed compilation +# databases (e.g. the Linux kernel) where a "value" may +# actually be the next flag: +# -main-file-name -mrelocation-model ... +# +# Lines starting with '#' are comments. Empty lines are ignored. +# The first matching rule wins; subsequent rules are not checked. +# +# You can provide additional patterns via --extra_arg_patterns . + +# --------------------------------------------------------------------------- +# Warning and diagnostic flags +# --------------------------------------------------------------------------- + +prefix -W +prefix -pedantic + +# --------------------------------------------------------------------------- +# Sanitizer and coverage flags +# --------------------------------------------------------------------------- + +prefix -fsanitize= +prefix -fcoverage-compilation-dir= +prefix -fcrash-diagnostics-dir= + +# --------------------------------------------------------------------------- +# Hardening / instrumentation flags +# --------------------------------------------------------------------------- + +prefix -ftrivial-auto-var-init= +prefix -fpatchable-function-entry= +prefix -fpatchable-function-entry-offset= +prefix -fstrict-flex-arrays= +prefix -mfunction-return= +exact -fskip-odr-check-in-gmf +exact -pic-is-pie +exact -mindirect-branch-cs-prefix + +# --------------------------------------------------------------------------- +# Internal compiler options (flag + one value) +# --------------------------------------------------------------------------- +# These always take exactly one following argument, even if that argument +# looks like a flag (e.g. -Xclang -emit-llvm). + +exact -Xclang 1 +exact -mllvm 1 + +# --------------------------------------------------------------------------- +# Internal compiler options (flag + one value, lenient) +# --------------------------------------------------------------------------- +# These expect one following argument, but in some compilation databases the +# "value" is actually the next flag. The ~1 modifier skips the following +# argument only when it does not start with '-'. + +exact -dependency-file ~1 +exact -diagnostic-log-file ~1 +exact -header-include-file ~1 +exact -stack-usage-file ~1 +exact -mrelocation-model ~1 +exact -pic-level ~1 +exact -main-file-name ~1 +exact -MT ~1 +exact -MQ ~1 + +# --------------------------------------------------------------------------- +# File-path options (matched by substring) +# --------------------------------------------------------------------------- +# Catches a broad range of -f*-file=, -f*-seed-file=, etc. + +contains -file= + +# --------------------------------------------------------------------------- +# Remaining options with embedded values (prefix=value form) +# --------------------------------------------------------------------------- + +prefix -dependent-lib= +prefix -stats-file= +prefix -fprofile-list= +prefix -fxray-always-instrument= +prefix -fxray-never-instrument= +prefix -fxray-attr-list= +prefix -tsan-compound-read-before-write= +prefix -tsan-distinguish-volatile= +prefix -treat +prefix -split-threshold-for-reg-with-hint= +prefix -instcombine-lower-dbg-declare= + +# =========================================================================== +# +# GCC-specific flags not recognized by Clang +# +# These appear in compile_commands.json from GCC-built projects (e.g. the +# Linux kernel) and cause "unknown argument" errors in Clang. +# +# =========================================================================== + +# --------------------------------------------------------------------------- +# GCC --param tuning knobs (entire family is GCC-only) +# --------------------------------------------------------------------------- +# --param=name=value or --param name=value + +prefix --param= +exact --param 1 + +# --------------------------------------------------------------------------- +# GCC spec files (entire family is GCC-only) +# --------------------------------------------------------------------------- + +prefix -specs= +exact -specs 1 +prefix --specs= +exact --specs 1 + +# --------------------------------------------------------------------------- +# GCC plugin system (incompatible with Clang's plugin system) +# --------------------------------------------------------------------------- + +prefix -fplugin= +prefix -fplugin-arg- + +# --------------------------------------------------------------------------- +# GCC interprocedural analysis passes (entire family is GCC-only) +# --------------------------------------------------------------------------- + +prefix -fipa- +prefix -fno-ipa- + +# --------------------------------------------------------------------------- +# GCC dump / optimization-info flags (entire family is GCC-only) +# --------------------------------------------------------------------------- + +prefix -fdump-rtl- +prefix -fdump-tree- +prefix -fdump-ipa- +prefix -fopt-info + +# --------------------------------------------------------------------------- +# GCC optimization flags with no Clang equivalent +# --------------------------------------------------------------------------- +# NOTE: -ftree-vectorize and -ftree-slp-vectorize ARE valid Clang aliases +# (for -fvectorize and -fslp-vectorize), so we cannot blanket-strip -ftree-*. + +exact -fno-var-tracking +exact -fno-var-tracking-assignments +exact -fconserve-stack +exact -fno-allow-store-data-races +exact -fallow-store-data-races +exact -fno-code-hoisting +exact -fno-tree-loop-im +exact -fno-tree-loop-ivcanon +exact -fno-tree-loop-distribute-patterns +exact -fno-tree-loop-distribution +exact -fno-tree-switch-conversion +exact -fno-tree-tail-merge +exact -fno-tree-pre +exact -fno-tree-dse +exact -fno-partial-inlining +exact -fno-inline-functions-called-once +exact -fno-hoist-adjacent-loads +exact -fno-guess-branch-probability +exact -fno-tracer +exact -fno-caller-saves +exact -fno-defer-pop +exact -fno-function-cse +exact -fno-peephole2 +exact -fno-gcse +exact -fno-gcse-lm +exact -fno-gcse-sm +exact -fno-reorder-blocks +exact -fno-reorder-blocks-and-partition +exact -fno-reorder-functions +exact -fno-toplevel-reorder +exact -fno-unit-at-a-time +exact -fno-live-range-shrinkage +exact -fno-devirtualize-speculatively +exact -fno-enforce-eh-specs +exact -fweb +exact -fno-web +exact -frename-registers +exact -fno-rename-registers +exact -fno-strength-reduce + +# GCC Graphite (polyhedral) loop optimization flags. +exact -fgraphite +exact -fgraphite-identity +exact -floop-nest-optimize +exact -floop-parallelize-all + +# --------------------------------------------------------------------------- +# GCC scheduler flags (no Clang equivalent) +# --------------------------------------------------------------------------- + +exact -fno-schedule-insns +exact -fno-schedule-insns2 +exact -fno-sched-pressure +exact -fsched-pressure +exact -fsched2-use-superblocks +exact -fsched-stalled-insns +prefix -fsched-stalled-insns= +exact -fsched-stalled-insns-dep +prefix -fsched-stalled-insns-dep= + +# --------------------------------------------------------------------------- +# GCC alignment flags (extended syntax not supported by Clang) +# --------------------------------------------------------------------------- +# NOTE: -falign-functions[=N] and -falign-loops=N ARE functional in Clang, +# so we only strip the forms that Clang does not support. + +prefix -falign-jumps +prefix -falign-labels + +# --------------------------------------------------------------------------- +# GCC LTO flags with no Clang equivalent +# --------------------------------------------------------------------------- + +prefix -flto-partition= +exact -fuse-linker-plugin +exact -fno-use-linker-plugin + +# --------------------------------------------------------------------------- +# GCC profile flags with no Clang equivalent +# --------------------------------------------------------------------------- + +exact -fprofile-correction +prefix -fprofile-dir= + +# --------------------------------------------------------------------------- +# GCC-specific machine flags (x86) +# --------------------------------------------------------------------------- +# These are common in Linux kernel compile_commands.json. + +exact -mrecord-mcount +exact -mnop-mcount +exact -mfentry +exact -mno-fp-ret-in-387 +exact -mno-80387 +exact -maccumulate-outgoing-args +exact -mskip-rax-setup +prefix -mpreferred-stack-boundary= +exact -mno-avx256-split-unaligned-load +exact -mno-avx256-split-unaligned-store +exact -mindirect-branch-register +prefix -mindirect-branch= +prefix -mharden-sls= + +# --------------------------------------------------------------------------- +# GCC diagnostic generation flags +# --------------------------------------------------------------------------- + +exact -fdiagnostics-generate-patch +prefix -ftrack-macro-expansion + +# =========================================================================== +# +# Removed or deprecated Clang flags +# +# These were accepted by older Clang versions but cause errors in newer ones. +# +# =========================================================================== + +# Removed in Clang 16 (old/new pass manager flags). +exact -fexperimental-new-pass-manager +exact -fno-experimental-new-pass-manager +exact -flegacy-pass-manager +exact -fno-legacy-pass-manager +exact -analyzer-store ~1 +exact -analyzer-opt-analyze-nested-blocks + +# Removed in Clang 17. +exact -fmodules-ts +exact -fcoroutines-ts + +# Removed in Clang 18. +exact -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang + +# Removed in Clang 21. +exact -frelaxed-template-template-args +exact -fno-relaxed-template-template-args