Skip to content

Commit

Permalink
Add clang source minimizer that reduces source to directives
Browse files Browse the repository at this point in the history
that might affect the dependency list for a compilation

This commit introduces a dependency directives source minimizer to clang
that minimizes header and source files to the minimum necessary preprocessor
directives for evaluating includes. It reduces the source down to #define, #include,

The source minimizer works by lexing the input with a custom fast lexer that recognizes
the preprocessor directives it cares about, and emitting those directives in the minimized source.
It ignores source code, comments, and normalizes whitespace. It gives up and fails if seems
any directives that it doesn't recognize as valid (e.g. #define 0).

In addition to the source minimizer this patch adds a
-print-dependency-directives-minimized-source CC1 option that allows you to invoke the minimizer
from clang directly.

Differential Revision: https://reviews.llvm.org/D55463

llvm-svn: 362459
  • Loading branch information
hyp committed Jun 3, 2019
1 parent 1f80306 commit 6e2d36b
Show file tree
Hide file tree
Showing 16 changed files with 1,443 additions and 2 deletions.
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticLexKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -818,4 +818,13 @@ def err_pp_eof_in_assume_nonnull : Error<

}

let CategoryName = "Dependency Directive Source Minimization Issue" in {

def err_dep_source_minimizer_missing_sema_after_at_import : Error<
"could not find ';' after @import">;
def err_dep_source_minimizer_unexpected_tokens_at_import : Error<
"unexpected extra tokens at end of @import declaration">;

}

}
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/CC1Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,9 @@ def migrate : Flag<["-"], "migrate">,
HelpText<"Migrate source code">;
def compiler_options_dump : Flag<["-"], "compiler-options-dump">,
HelpText<"Dump the compiler configuration options">;
def print_dependency_directives_minimized_source : Flag<["-"],
"print-dependency-directives-minimized-source">,
HelpText<"Print the output of the dependency directives source minimizer">;
}

def emit_llvm_uselists : Flag<["-"], "emit-llvm-uselists">,
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Frontend/FrontendActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,17 @@ class PrintPreambleAction : public FrontendAction {
bool usesPreprocessorOnly() const override { return true; }
};

class PrintDependencyDirectivesSourceMinimizerAction : public FrontendAction {
protected:
void ExecuteAction() override;
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &,
StringRef) override {
return nullptr;
}

bool usesPreprocessorOnly() const override { return true; }
};

//===----------------------------------------------------------------------===//
// Preprocessor Actions
//===----------------------------------------------------------------------===//
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Frontend/FrontendOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ enum ActionKind {
MigrateSource,

/// Just lex, no output.
RunPreprocessorOnly
RunPreprocessorOnly,

/// Print the output of the dependency directives source minimizer.
PrintDependencyDirectivesSourceMinimizerOutput
};

} // namespace frontend
Expand Down
88 changes: 88 additions & 0 deletions clang/include/clang/Lex/DependencyDirectivesSourceMinimizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//===- clang/Lex/DependencyDirectivesSourceMinimizer.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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This is the interface for minimizing header and source files to the
/// minimum necessary preprocessor directives for evaluating includes. It
/// reduces the source down to #define, #include, #import, @import, and any
/// conditional preprocessor logic that contains one of those.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LEX_DEPENDENCY_DIRECTIVES_SOURCE_MINIMIZER_H
#define LLVM_CLANG_LEX_DEPENDENCY_DIRECTIVES_SOURCE_MINIMIZER_H

#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

namespace clang {

class DiagnosticsEngine;

namespace minimize_source_to_dependency_directives {

/// Represents the kind of preprocessor directive or a module declaration that
/// is tracked by the source minimizer in its token output.
enum TokenKind {
pp_none,
pp_include,
pp___include_macros,
pp_define,
pp_undef,
pp_import,
pp_pragma_import,
pp_include_next,
pp_if,
pp_ifdef,
pp_ifndef,
pp_elif,
pp_else,
pp_endif,
decl_at_import,
pp_eof,
};

/// Represents a simplified token that's lexed as part of the source
/// minimization. It's used to track the location of various preprocessor
/// directives that could potentially have an effect on the depedencies.
struct Token {
/// The kind of token.
TokenKind K = pp_none;

/// Offset into the output byte stream of where the directive begins.
int Offset = -1;

Token(TokenKind K, int Offset) : K(K), Offset(Offset) {}
};

} // end namespace minimize_source_to_dependency_directives

/// Minimize the input down to the preprocessor directives that might have
/// an effect on the dependencies for a compilation unit.
///
/// This function deletes all non-preprocessor code, and strips anything that
/// can't affect what gets included. It canonicalizes whitespace where
/// convenient to stabilize the output against formatting changes in the input.
///
/// Clears the output vectors at the beginning of the call.
///
/// \returns false on success, true on error. If the diagnostic engine is not
/// null, an appropriate error is reported using the given input location
/// with the offset that corresponds to the minimizer's current buffer offset.
bool minimizeSourceToDependencyDirectives(
llvm::StringRef Input, llvm::SmallVectorImpl<char> &Output,
llvm::SmallVectorImpl<minimize_source_to_dependency_directives::Token>
&Tokens,
DiagnosticsEngine *Diags = nullptr,
SourceLocation InputSourceLoc = SourceLocation());

} // end namespace clang

#endif // LLVM_CLANG_LEX_DEPENDENCY_DIRECTIVES_SOURCE_MINIMIZER_H
5 changes: 5 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1696,6 +1696,10 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.ProgramAction = frontend::MigrateSource; break;
case OPT_Eonly:
Opts.ProgramAction = frontend::RunPreprocessorOnly; break;
case OPT_print_dependency_directives_minimized_source:
Opts.ProgramAction =
frontend::PrintDependencyDirectivesSourceMinimizerOutput;
break;
}
}

Expand Down Expand Up @@ -3116,6 +3120,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
case frontend::PrintPreprocessedInput:
case frontend::RewriteMacros:
case frontend::RunPreprocessorOnly:
case frontend::PrintDependencyDirectivesSourceMinimizerOutput:
return true;
}
llvm_unreachable("invalid frontend action");
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/Utils.h"
#include "clang/Lex/DependencyDirectivesSourceMinimizer.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
Expand All @@ -23,8 +24,8 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <system_error>

Expand Down Expand Up @@ -908,3 +909,33 @@ void DumpCompilerOptionsAction::ExecuteAction() {

OS << "}";
}

void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
CompilerInstance &CI = getCompilerInstance();
SourceManager &SM = CI.getPreprocessor().getSourceManager();
const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID());

llvm::SmallString<1024> Output;
llvm::SmallVector<minimize_source_to_dependency_directives::Token, 32> Toks;
if (minimizeSourceToDependencyDirectives(
FromFile->getBuffer(), Output, Toks, &CI.getDiagnostics(),
SM.getLocForStartOfFile(SM.getMainFileID()))) {
assert(CI.getDiagnostics().hasErrorOccurred() &&
"no errors reported for failure");

// Preprocess the source when verifying the diagnostics to capture the
// 'expected' comments.
if (CI.getDiagnosticOpts().VerifyDiagnostics) {
// Make sure we don't emit new diagnostics!
CI.getDiagnostics().setSuppressAllDiagnostics();
Preprocessor &PP = getCompilerInstance().getPreprocessor();
PP.EnterMainSourceFile();
Token Tok;
do {
PP.Lex(Tok);
} while (Tok.isNot(tok::eof));
}
return;
}
llvm::outs() << Output;
}
2 changes: 2 additions & 0 deletions clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
case RunAnalysis: Action = "RunAnalysis"; break;
#endif
case RunPreprocessorOnly: return llvm::make_unique<PreprocessOnlyAction>();
case PrintDependencyDirectivesSourceMinimizerOutput:
return llvm::make_unique<PrintDependencyDirectivesSourceMinimizerAction>();
}

#if !CLANG_ENABLE_ARCMT || !CLANG_ENABLE_STATIC_ANALYZER \
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Lex/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
set(LLVM_LINK_COMPONENTS support)

add_clang_library(clangLex
DependencyDirectivesSourceMinimizer.cpp
HeaderMap.cpp
HeaderSearch.cpp
Lexer.cpp
Expand Down
Loading

0 comments on commit 6e2d36b

Please sign in to comment.