Improve PCH handling (#136)

This commit is contained in:
ykiko
2025-06-10 22:33:17 +08:00
committed by GitHub
parent 0c67c14eef
commit bbece60524
7 changed files with 347 additions and 269 deletions

View File

@@ -6,7 +6,7 @@
namespace clice {
namespace impl {
namespace {
std::unique_ptr<clang::CompilerInvocation> createInvocation(CompilationParams& params) {
llvm::SmallString<1024> buffer;
@@ -91,10 +91,6 @@ std::unique_ptr<clang::CompilerInstance> createInstance(CompilationParams& param
return instance;
}
} // namespace impl
namespace {
/// Execute given action with the on the given instance. `callback` is called after
/// `BeginSourceFile`. Beacuse `BeginSourceFile` may create new preprocessor.
std::expected<void, std::string> ExecuteAction(clang::CompilerInstance& instance,
@@ -166,14 +162,19 @@ std::expected<ASTInfo, std::string> ExecuteAction(std::unique_ptr<clang::Compile
} // namespace
std::expected<ASTInfo, std::string> preprocess(CompilationParams& params) {
auto instance = createInstance(params);
return ExecuteAction(std::move(instance), std::make_unique<clang::PreprocessOnlyAction>());
}
std::expected<ASTInfo, std::string> compile(CompilationParams& params) {
auto instance = impl::createInstance(params);
auto instance = createInstance(params);
return ExecuteAction(std::move(instance), std::make_unique<clang::SyntaxOnlyAction>());
}
std::expected<ASTInfo, std::string> compile(CompilationParams& params,
clang::CodeCompleteConsumer* consumer) {
auto instance = impl::createInstance(params);
auto instance = createInstance(params);
auto& [file, offset] = params.completion;
@@ -211,7 +212,7 @@ std::expected<ASTInfo, std::string> compile(CompilationParams& params,
std::expected<ASTInfo, std::string> compile(CompilationParams& params, PCHInfo& out) {
assert(params.bound.has_value() && "Preamble bounds is required to build PCH");
auto instance = impl::createInstance(params);
auto instance = createInstance(params);
llvm::StringRef outPath = params.outPath.str();
@@ -234,13 +235,12 @@ std::expected<ASTInfo, std::string> compile(CompilationParams& params, PCHInfo&
}
std::expected<ASTInfo, std::string> compile(CompilationParams& params, PCMInfo& out) {
auto instance = impl::createInstance(params);
auto instance = createInstance(params);
/// Set options to generate PCM.
instance->getFrontendOpts().OutputFile = params.outPath.str();
instance->getFrontendOpts().ProgramAction = clang::frontend::GenerateReducedModuleInterface;
;
if(auto info = ExecuteAction(std::move(instance),
std::make_unique<clang::GenerateReducedModuleInterfaceAction>())) {
assert(info->pp().isInNamedInterfaceUnit() &&

View File

@@ -7,73 +7,60 @@ namespace clice {
namespace {
struct PPCallback : public clang::PPCallbacks {
clang::FileID prevFID;
clang::Preprocessor& PP;
clang::SourceManager& SM;
llvm::DenseMap<clang::FileID, Directive>& directives;
llvm::DenseMap<clang::MacroInfo*, std::size_t> macroCache;
PPCallback(clang::Preprocessor& PP, llvm::DenseMap<clang::FileID, Directive>& directives) :
class DirectiveCollector : public clang::PPCallbacks {
public:
DirectiveCollector(clang::Preprocessor& PP,
llvm::DenseMap<clang::FileID, Directive>& directives) :
PP(PP), SM(PP.getSourceManager()), directives(directives) {}
void addCondition(clang::SourceLocation loc,
Condition::BranchKind kind,
Condition::ConditionValue value,
clang::SourceRange conditionRange) {
auto& directive = directives[PP.getSourceManager().getFileID(loc)];
directive.conditions.emplace_back(Condition{kind, value, loc, conditionRange});
private:
void add_condition(clang::SourceLocation location,
Condition::BranchKind kind,
Condition::ConditionValue value,
clang::SourceRange cond_range) {
auto& directive = directives[SM.getFileID(location)];
directive.conditions.emplace_back(kind, value, location, cond_range);
}
void addCondition(clang::SourceLocation loc,
Condition::BranchKind kind,
clang::PPCallbacks::ConditionValueKind value,
clang::SourceRange conditionRange) {
Condition::ConditionValue condValue = Condition::None;
switch(value) {
case clang::PPCallbacks::CVK_False: {
condValue = Condition::False;
break;
}
case clang::PPCallbacks::CVK_True: {
condValue = Condition::True;
break;
}
case clang::PPCallbacks::CVK_NotEvaluated: {
condValue = Condition::Skipped;
break;
}
}
addCondition(loc, kind, condValue, conditionRange);
void add_condition(clang::SourceLocation loc,
Condition::BranchKind kind,
clang::PPCallbacks::ConditionValueKind value,
clang::SourceRange conditionRange) {
Condition::ConditionValue cond_value =
value == clang::PPCallbacks::CVK_False ? Condition::None
: value == clang::PPCallbacks::CVK_True ? Condition::True
: value == clang::PPCallbacks::CVK_NotEvaluated ? Condition::Skipped
: Condition::None;
add_condition(loc, kind, cond_value, conditionRange);
}
void addCondition(clang::SourceLocation loc,
Condition::BranchKind kind,
const clang::Token& name,
const clang::MacroDefinition& definition) {
void add_condition(clang::SourceLocation loc,
Condition::BranchKind kind,
const clang::Token& name,
const clang::MacroDefinition& definition) {
if(auto def = definition.getMacroInfo()) {
addMacro(def, MacroRef::Ref, name.getLocation());
addCondition(loc, kind, Condition::True, name.getLocation());
add_macro(def, MacroRef::Ref, name.getLocation());
add_condition(loc, kind, Condition::True, name.getLocation());
} else {
addCondition(loc, kind, Condition::False, name.getLocation());
add_condition(loc, kind, Condition::False, name.getLocation());
}
}
void addMacro(const clang::MacroInfo* def, MacroRef::Kind kind, clang::SourceLocation loc) {
void add_macro(const clang::MacroInfo* def, MacroRef::Kind kind, clang::SourceLocation loc) {
if(def->isBuiltinMacro()) {
return;
}
if(PP.getSourceManager().isWrittenInBuiltinFile(loc) ||
PP.getSourceManager().isWrittenInCommandLineFile(loc) ||
PP.getSourceManager().isWrittenInScratchSpace(loc)) {
if(SM.isWrittenInBuiltinFile(loc) || SM.isWrittenInCommandLineFile(loc) ||
SM.isWrittenInScratchSpace(loc)) {
return;
}
auto& directive = directives[PP.getSourceManager().getFileID(loc)];
auto& directive = directives[SM.getFileID(loc)];
directive.macros.emplace_back(MacroRef{def, kind, loc});
}
public:
/// ============================================================================
/// Rewritten Preprocessor Callbacks
/// ============================================================================
@@ -130,6 +117,18 @@ struct PPCallback : public clang::PPCallbacks {
}
}
void moduleImport(clang::SourceLocation import_location,
clang::ModuleIdPath names,
const clang::Module*) override {
auto fid = SM.getFileID(SM.getExpansionLoc(import_location));
auto& import = directives[fid].imports.emplace_back();
import.location = import_location;
for(auto& [name, location]: names) {
import.name += name->getName();
import.name_locations.emplace_back(location);
}
}
void HasInclude(clang::SourceLocation location,
llvm::StringRef,
bool,
@@ -167,71 +166,71 @@ struct PPCallback : public clang::PPCallbacks {
}
void If(clang::SourceLocation loc,
clang::SourceRange conditionRange,
clang::SourceRange cond_range,
clang::PPCallbacks::ConditionValueKind value) override {
addCondition(loc, Condition::If, value, conditionRange);
add_condition(loc, Condition::If, value, cond_range);
}
void Elif(clang::SourceLocation loc,
clang::SourceRange conditionRange,
clang::SourceRange cond_range,
clang::PPCallbacks::ConditionValueKind value,
clang::SourceLocation) override {
addCondition(loc, Condition::Elif, value, conditionRange);
add_condition(loc, Condition::Elif, value, cond_range);
}
void Ifdef(clang::SourceLocation loc,
const clang::Token& name,
const clang::MacroDefinition& definition) override {
addCondition(loc, Condition::Ifdef, name, definition);
add_condition(loc, Condition::Ifdef, name, definition);
}
/// Invoke when #elifdef branch is taken.
void Elifdef(clang::SourceLocation loc,
const clang::Token& name,
const clang::MacroDefinition& definition) override {
addCondition(loc, Condition::Elifdef, name, definition);
add_condition(loc, Condition::Elifdef, name, definition);
}
/// Invoke when #elif is skipped.
void Elifdef(clang::SourceLocation loc,
clang::SourceRange conditionRange,
clang::SourceRange cond_range,
clang::SourceLocation) override {
/// FIXME: should we try to evaluate the condition to compute the macro reference?
addCondition(loc, Condition::Elifdef, Condition::Skipped, conditionRange);
add_condition(loc, Condition::Elifdef, Condition::Skipped, cond_range);
}
/// Invoke when #ifndef is taken.
void Ifndef(clang::SourceLocation loc,
const clang::Token& name,
const clang::MacroDefinition& definition) override {
addCondition(loc, Condition::Ifndef, name, definition);
add_condition(loc, Condition::Ifndef, name, definition);
}
// Invoke when #elifndef is taken.
void Elifndef(clang::SourceLocation loc,
const clang::Token& name,
const clang::MacroDefinition& definition) override {
addCondition(loc, Condition::Elifndef, name, definition);
add_condition(loc, Condition::Elifndef, name, definition);
}
// Invoke when #elifndef is skipped.
void Elifndef(clang::SourceLocation loc,
clang::SourceRange conditionRange,
clang::SourceRange cond_range,
clang::SourceLocation) override {
addCondition(loc, Condition::Elifndef, Condition::Skipped, conditionRange);
add_condition(loc, Condition::Elifndef, Condition::Skipped, cond_range);
}
void Else(clang::SourceLocation loc, clang::SourceLocation ifLoc) override {
addCondition(loc, Condition::Else, Condition::None, clang::SourceRange());
add_condition(loc, Condition::Else, Condition::None, clang::SourceRange());
}
void Endif(clang::SourceLocation loc, clang::SourceLocation ifLoc) override {
addCondition(loc, Condition::EndIf, Condition::None, clang::SourceRange());
add_condition(loc, Condition::EndIf, Condition::None, clang::SourceRange());
}
void MacroDefined(const clang::Token& name, const clang::MacroDirective* MD) override {
if(auto def = MD->getMacroInfo()) {
addMacro(def, MacroRef::Def, name.getLocation());
add_macro(def, MacroRef::Def, name.getLocation());
}
}
@@ -240,7 +239,7 @@ struct PPCallback : public clang::PPCallbacks {
clang::SourceRange range,
const clang::MacroArgs* args) override {
if(auto def = definition.getMacroInfo()) {
addMacro(def, MacroRef::Ref, name.getLocation());
add_macro(def, MacroRef::Ref, name.getLocation());
}
}
@@ -248,16 +247,23 @@ struct PPCallback : public clang::PPCallbacks {
const clang::MacroDefinition& MD,
const clang::MacroDirective* undef) override {
if(auto def = MD.getMacroInfo()) {
addMacro(def, MacroRef::Undef, name.getLocation());
add_macro(def, MacroRef::Undef, name.getLocation());
}
}
private:
clang::FileID prevFID;
clang::Preprocessor& PP;
clang::SourceManager& SM;
llvm::DenseMap<clang::FileID, Directive>& directives;
llvm::DenseMap<clang::MacroInfo*, std::size_t> macroCache;
};
} // namespace
void Directive::attach(clang::Preprocessor& pp,
llvm::DenseMap<clang::FileID, Directive>& directives) {
pp.addPPCallbacks(std::make_unique<PPCallback>(pp, directives));
pp.addPPCallbacks(std::make_unique<DirectiveCollector>(pp, directives));
}
} // namespace clice

View File

@@ -97,33 +97,16 @@ std::string scanModuleName(CompilationParams& params) {
}
std::expected<ModuleInfo, std::string> scanModule(CompilationParams& params) {
struct ModuleCollector : public clang::PPCallbacks {
ModuleInfo& info;
ModuleCollector(ModuleInfo& info) : info(info) {}
void moduleImport(clang::SourceLocation importLoc,
clang::ModuleIdPath path,
const clang::Module* imported) override {
assert(path.size() == 1);
info.mods.emplace_back(path[0].first->getName());
}
};
ModuleInfo info;
clang::PreprocessOnlyAction action;
auto instance = impl::createInstance(params);
if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) {
return std::unexpected("Failed to begin source file");
auto AST = preprocess(params);
if(!AST) {
return std::unexpected(AST.error());
}
auto& pp = instance->getPreprocessor();
auto&& pp = AST->pp();
pp.addPPCallbacks(std::make_unique<ModuleCollector>(info));
if(auto error = action.Execute()) {
return std::unexpected(std::format("{}", error));
for(auto& import: AST->directives()[AST->getInterestedFile()].imports) {
info.mods.emplace_back(import.name);
}
if(pp.isInNamedModule()) {